diff --git a/.github/docs/Application_configurations.md b/.github/docs/Application_configurations.md index eb7e1f1c..753305bd 100644 --- a/.github/docs/Application_configurations.md +++ b/.github/docs/Application_configurations.md @@ -66,4 +66,5 @@ RTL_CONFIG_PATH (Path for the folder containing 'RTL-Config.json' file, Required BITCOIND_CONFIG_PATH (Full path of the bitcoind.conf file including the file name, Optional)
CHANNEL_BACKUP_PATH (Folder location for saving the channel backup files, valid for LND implementation only, Required if ln implementation=LND else Optional)
ENABLE_OFFERS (Boolean flag to enable the offers feature on core lighning, default false, optional)
+ENABLE_PEERSWAP (Boolean flag to enable the peerswap feature on core lighning, default false, optional)
LN_API_PASSWORD (Password for Eclair implementation if the eclair.conf path is not available, Required if ln implementation=ECL && config path is undefined)
diff --git a/backend/controllers/shared/RTLConf.js b/backend/controllers/shared/RTLConf.js index 1b693853..c42dd329 100644 --- a/backend/controllers/shared/RTLConf.js +++ b/backend/controllers/shared/RTLConf.js @@ -108,6 +108,7 @@ export const getRTLConfig = (req, res, next) => { settings.swapServerUrl = node.swap_server_url; settings.boltzServerUrl = node.boltz_server_url; settings.enableOffers = node.enable_offers; + settings.enablePeerswap = node.enable_peerswap; settings.channelBackupPath = node.channel_backup_path; settings.currencyUnit = node.currency_unit; nodesArr.push({ @@ -311,7 +312,7 @@ export const updateServiceSettings = (req, res, next) => { const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const selectedNode = common.findNode(req.session.selectedNode.index); - config.nodes.find((node) => { + config.nodes.forEach((node) => { if (node.index === req.session.selectedNode.index) { switch (req.body.service) { case 'LOOP': @@ -346,6 +347,10 @@ export const updateServiceSettings = (req, res, next) => { node.Settings.enableOffers = req.body.settings.enableOffers; selectedNode.enable_offers = req.body.settings.enableOffers; break; + case 'PEERSWAP': + node.Settings.enablePeerswap = req.body.settings.enablePeerswap; + selectedNode.enable_peerswap = req.body.settings.enablePeerswap; + break; default: break; } diff --git a/backend/models/config.model.js b/backend/models/config.model.js index 3191329f..f1911ec6 100644 --- a/backend/models/config.model.js +++ b/backend/models/config.model.js @@ -1,5 +1,5 @@ export class CommonSelectedNode { - constructor(options, ln_server_url, macaroon_path, ln_api_password, swap_server_url, boltz_server_url, config_path, rtl_conf_file_path, swap_macaroon_path, boltz_macaroon_path, bitcoind_config_path, channel_backup_path, log_level, log_file, index, ln_node, ln_implementation, user_persona, theme_mode, theme_color, fiat_conversion, currency_unit, ln_version, api_version, enable_offers) { + constructor(options, ln_server_url, macaroon_path, ln_api_password, swap_server_url, boltz_server_url, config_path, rtl_conf_file_path, swap_macaroon_path, boltz_macaroon_path, bitcoind_config_path, channel_backup_path, log_level, log_file, index, ln_node, ln_implementation, user_persona, theme_mode, theme_color, fiat_conversion, currency_unit, ln_version, api_version, enable_offers, enable_peerswap) { this.options = options; this.ln_server_url = ln_server_url; this.macaroon_path = macaroon_path; @@ -25,6 +25,7 @@ export class CommonSelectedNode { this.ln_version = ln_version; this.api_version = api_version; this.enable_offers = enable_offers; + this.enable_peerswap = enable_peerswap; } } export class AuthenticationConfiguration { @@ -35,7 +36,7 @@ export class AuthenticationConfiguration { } } export class NodeSettingsConfiguration { - constructor(userPersona, themeMode, themeColor, fiatConversion, currencyUnit, bitcoindConfigPath, logLevel, lnServerUrl, swapServerUrl, boltzServerUrl, channelBackupPath, enableOffers) { + constructor(userPersona, themeMode, themeColor, fiatConversion, currencyUnit, bitcoindConfigPath, logLevel, lnServerUrl, swapServerUrl, boltzServerUrl, channelBackupPath, enableOffers, enablePeerswap) { this.userPersona = userPersona; this.themeMode = themeMode; this.themeColor = themeColor; @@ -48,6 +49,7 @@ export class NodeSettingsConfiguration { this.boltzServerUrl = boltzServerUrl; this.channelBackupPath = channelBackupPath; this.enableOffers = enableOffers; + this.enablePeerswap = enablePeerswap; } } export class LogJSONObj { diff --git a/backend/utils/config.js b/backend/utils/config.js index d06d40f9..3bbe8822 100644 --- a/backend/utils/config.js +++ b/backend/utils/config.js @@ -241,6 +241,7 @@ export class ConfigService { this.common.nodes[idx].boltz_macaroon_path = ''; } this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS ? process.env.ENABLE_OFFERS : (node.Settings.enableOffers) ? node.Settings.enableOffers : false; + this.common.nodes[idx].enable_peerswap = process.env.ENABLE_PEERSWAP ? process.env.ENABLE_PEERSWAP : (node.Settings.enablePeerswap) ? node.Settings.enablePeerswap : false; this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : ''; this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH ? process.env.CHANNEL_BACKUP_PATH : (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; try { diff --git a/server/controllers/shared/RTLConf.ts b/server/controllers/shared/RTLConf.ts index 4dca8407..db0e3fd2 100644 --- a/server/controllers/shared/RTLConf.ts +++ b/server/controllers/shared/RTLConf.ts @@ -109,6 +109,7 @@ export const getRTLConfig = (req, res, next) => { settings.swapServerUrl = node.swap_server_url; settings.boltzServerUrl = node.boltz_server_url; settings.enableOffers = node.enable_offers; + settings.enablePeerswap = node.enable_peerswap; settings.channelBackupPath = node.channel_backup_path; settings.currencyUnit = node.currency_unit; nodesArr.push({ @@ -306,7 +307,7 @@ export const updateServiceSettings = (req, res, next) => { const RTLConfFile = common.rtl_conf_file_path + sep + 'RTL-Config.json'; const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8')); const selectedNode = common.findNode(req.session.selectedNode.index); - config.nodes.find((node) => { + config.nodes.forEach((node) => { if (node.index === req.session.selectedNode.index) { switch (req.body.service) { case 'LOOP': @@ -342,6 +343,11 @@ export const updateServiceSettings = (req, res, next) => { selectedNode.enable_offers = req.body.settings.enableOffers; break; + case 'PEERSWAP': + node.Settings.enablePeerswap = req.body.settings.enablePeerswap; + selectedNode.enable_peerswap = req.body.settings.enablePeerswap; + break; + default: break; } diff --git a/server/models/config.model.ts b/server/models/config.model.ts index 45cb15e9..c166eb09 100644 --- a/server/models/config.model.ts +++ b/server/models/config.model.ts @@ -25,7 +25,8 @@ export class CommonSelectedNode { public currency_unit?: string, public ln_version?: string, public api_version?: string, - public enable_offers?: boolean + public enable_offers?: boolean, + public enable_peerswap?: boolean ) { } } @@ -54,7 +55,8 @@ export class NodeSettingsConfiguration { public swapServerUrl?: string, public boltzServerUrl?: string, public channelBackupPath?: string, - public enableOffers?: boolean + public enableOffers?: boolean, + public enablePeerswap?: boolean ) { } } diff --git a/server/utils/config.ts b/server/utils/config.ts index fe0d3f2b..cb2c3e3d 100644 --- a/server/utils/config.ts +++ b/server/utils/config.ts @@ -226,6 +226,7 @@ export class ConfigService { this.common.nodes[idx].boltz_macaroon_path = ''; } this.common.nodes[idx].enable_offers = process.env.ENABLE_OFFERS ? process.env.ENABLE_OFFERS : (node.Settings.enableOffers) ? node.Settings.enableOffers : false; + this.common.nodes[idx].enable_peerswap = process.env.ENABLE_PEERSWAP ? process.env.ENABLE_PEERSWAP : (node.Settings.enablePeerswap) ? node.Settings.enablePeerswap : false; this.common.nodes[idx].bitcoind_config_path = process.env.BITCOIND_CONFIG_PATH ? process.env.BITCOIND_CONFIG_PATH : (node.Settings.bitcoindConfigPath) ? node.Settings.bitcoindConfigPath : ''; this.common.nodes[idx].channel_backup_path = process.env.CHANNEL_BACKUP_PATH ? process.env.CHANNEL_BACKUP_PATH : (node.Settings.channelBackupPath) ? node.Settings.channelBackupPath : this.common.rtl_conf_file_path + sep + 'channels-backup' + sep + 'node-' + node.index; try { diff --git a/src/app/app.routing.ts b/src/app/app.routing.ts index d6098db1..c377bce6 100644 --- a/src/app/app.routing.ts +++ b/src/app/app.routing.ts @@ -11,15 +11,21 @@ import { NodeSettingsComponent } from './shared/components/node-config/node-sett 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'; -import { ServicesComponent } from './shared/components/services/services.component'; -import { LoopComponent } from './shared/components/services/loop/loop.component'; -import { BoltzRootComponent } from './shared/components/services/boltz/boltz-root.component'; +import { LNServicesComponent } from './shared/components/ln-services/ln-services.component'; +import { LoopComponent } from './shared/components/ln-services/loop/loop.component'; +import { BoltzRootComponent } from './shared/components/ln-services/boltz/boltz-root.component'; import { HelpComponent } from './shared/components/help/help.component'; import { LoginComponent } from './shared/components/login/login.component'; import { NotFoundComponent } from './shared/components/not-found/not-found.component'; import { ErrorComponent } from './shared/components/error/error.component'; import { AuthGuard } from './shared/services/auth.guard'; import { ExperimentalSettingsComponent } from './shared/components/node-config/experimental-settings/experimental-settings.component'; +import { PeerswapComponent } from './shared/components/ln-services/peerswap/peerswap.component'; +import { PeerswapServiceSettingsComponent } from './shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component'; +import { SwapPeersComponent } from './shared/components/ln-services/peerswap/swap-peers/swap-peers.component'; +import { PeerswapsOutComponent } from './shared/components/ln-services/peerswap/swaps-out/swaps-out.component'; +import { PeerswapsInComponent } from './shared/components/ln-services/peerswap/swaps-in/swaps-in.component'; +import { PeerswapsCancelledComponent } from './shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component'; export const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: 'login' }, @@ -42,7 +48,8 @@ export const routes: Routes = [ path: 'services', component: ServicesSettingsComponent, canActivate: [AuthGuard], children: [ { path: '', pathMatch: 'full', redirectTo: 'loop' }, { path: 'loop', component: LoopServiceSettingsComponent, canActivate: [AuthGuard] }, - { path: 'boltz', component: BoltzServiceSettingsComponent, canActivate: [AuthGuard] } + { path: 'boltz', component: BoltzServiceSettingsComponent, canActivate: [AuthGuard] }, + { path: 'peerswap', component: PeerswapServiceSettingsComponent, canActivate: [AuthGuard] } ] }, { path: 'experimental', component: ExperimentalSettingsComponent, canActivate: [AuthGuard] }, @@ -50,12 +57,21 @@ export const routes: Routes = [ ] }, { - path: 'services', component: ServicesComponent, canActivate: [AuthGuard], children: [ + path: 'services', component: LNServicesComponent, canActivate: [AuthGuard], children: [ { path: '', pathMatch: 'full', redirectTo: 'loop' }, { path: 'loop', pathMatch: 'full', redirectTo: 'loop/loopout' }, { path: 'loop/:selTab', component: LoopComponent }, { path: 'boltz', pathMatch: 'full', redirectTo: 'boltz/swapout' }, - { path: 'boltz/:selTab', component: BoltzRootComponent } + { path: 'boltz/:selTab', component: BoltzRootComponent }, + { + path: 'peerswap', component: PeerswapComponent, canActivate: [AuthGuard], children: [ + { path: '', pathMatch: 'full', redirectTo: 'peers' }, + { path: 'peers', component: SwapPeersComponent, canActivate: [AuthGuard] }, + { path: 'psout', component: PeerswapsOutComponent, canActivate: [AuthGuard] }, + { path: 'psin', component: PeerswapsInComponent, canActivate: [AuthGuard] }, + { path: 'pscancelled', component: PeerswapsCancelledComponent, canActivate: [AuthGuard] } + ] + }, ] }, { path: 'help', component: HelpComponent }, diff --git a/src/app/cln/store/cln.state.ts b/src/app/cln/store/cln.state.ts index 6cb36265..ddd05b0d 100644 --- a/src/app/cln/store/cln.state.ts +++ b/src/app/cln/store/cln.state.ts @@ -45,7 +45,7 @@ export const initCLNState: CLNState = { FetchOffers: { status: APICallStatusEnum.UN_INITIATED }, FetchOfferBookmarks: { status: APICallStatusEnum.UN_INITIATED } }, - nodeSettings: { userPersona: UserPersonaEnum.OPERATOR, selCurrencyUnit: 'USD', fiatConversion: false, channelBackupPath: '', currencyUnits: [], enableOffers: false }, + nodeSettings: { userPersona: UserPersonaEnum.OPERATOR, selCurrencyUnit: 'USD', fiatConversion: false, channelBackupPath: '', currencyUnits: [], enableOffers: false, enablePeerswap: false }, information: {}, fees: {}, feeRatesPerKB: {}, diff --git a/src/app/shared/components/ln-services/peerswap/peerswap.component.html b/src/app/shared/components/ln-services/peerswap/peerswap.component.html new file mode 100755 index 00000000..afc58b70 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/peerswap.component.html @@ -0,0 +1,16 @@ +
+ + Peerswap +
+
+ + + +
+ +
+
+
+
diff --git a/src/app/shared/components/ln-services/peerswap/peerswap.component.scss b/src/app/shared/components/ln-services/peerswap/peerswap.component.scss new file mode 100755 index 00000000..e69de29b diff --git a/src/app/shared/components/ln-services/peerswap/peerswap.component.spec.ts b/src/app/shared/components/ln-services/peerswap/peerswap.component.spec.ts new file mode 100755 index 00000000..af225501 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/peerswap.component.spec.ts @@ -0,0 +1,52 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/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 { LoopService } from '../../../services/loop.service'; + +import { PeerswapComponent } from './peerswap.component'; +import { SharedModule } from '../../../shared.module'; +import { mockDataService } from '../../../test-helpers/mock-services'; +import { CommonService } from '../../../services/common.service'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { DataService } from '../../../services/data.service'; + +describe('PeerswapComponent', () => { + let component: PeerswapComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PeerswapComponent], + imports: [ + BrowserAnimationsModule, + SharedModule, + RouterTestingModule, + StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }) + ], + providers: [ + CommonService, + { provide: DataService, useClass: mockDataService } + ] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PeerswapComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/ln-services/peerswap/peerswap.component.ts b/src/app/shared/components/ln-services/peerswap/peerswap.component.ts new file mode 100755 index 00000000..f451bdca --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/peerswap.component.ts @@ -0,0 +1,41 @@ +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 { faHandshake } from '@fortawesome/free-solid-svg-icons'; + + +@Component({ + selector: 'rtl-peerswap', + templateUrl: './peerswap.component.html', + styleUrls: ['./peerswap.component.scss'] +}) +export class PeerswapComponent implements OnInit, OnDestroy { + + public faHandshake = faHandshake; + public links = [{ link: 'peers', name: 'Peers' }, { link: 'psout', name: 'Peerswap Out' }, { link: 'psin', name: 'Peerswap In' }, { link: 'pscancelled', name: 'Cancelled Peerswaps' }]; + public activeTab = this.links[0]; + private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject()]; + + constructor(private router: Router) { } + + ngOnInit() { + const linkFound = this.links.find((link) => this.router.url.includes(link.link)); + this.activeTab = linkFound ? linkFound : this.links[0]; + this.router.events.pipe(takeUntil(this.unSubs[0]), filter((e) => e instanceof ResolveEnd)). + subscribe({ + next: (value: ResolveEnd | Event) => { + const linkFound = this.links.find((link) => (value).urlAfterRedirects.includes(link.link)); + this.activeTab = linkFound ? linkFound : this.links[0]; + } + }); + } + + ngOnDestroy() { + this.unSubs.forEach((completeSub) => { + completeSub.next(null); + completeSub.complete(); + }); + } + +} diff --git a/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.html b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.html new file mode 100755 index 00000000..26568364 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.html @@ -0,0 +1 @@ +

Swap Peers

\ No newline at end of file diff --git a/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.scss b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.scss new file mode 100755 index 00000000..e69de29b diff --git a/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.spec.ts b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.spec.ts new file mode 100755 index 00000000..6bc0b05f --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.spec.ts @@ -0,0 +1,35 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { SharedModule } from '../../../../shared.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { SwapPeersComponent } from './swap-peers.component'; + +describe('SwapPeersComponent', () => { + let component: SwapPeersComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [SwapPeersComponent], + imports: [ + BrowserAnimationsModule, + SharedModule + ], + providers: [] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SwapPeersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.ts b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.ts new file mode 100755 index 00000000..e2f009f1 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swap-peers/swap-peers.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'rtl-peerswap-peers', + templateUrl: './swap-peers.component.html', + styleUrls: ['./swap-peers.component.scss'] +}) +export class SwapPeersComponent implements OnInit, OnDestroy { + + private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject()]; + + constructor() {} + + ngOnInit() { + } + + ngOnDestroy() { + this.unSubs.forEach((completeSub) => { + completeSub.next(null); + completeSub.complete(); + }); + } + +} diff --git a/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.html b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.html new file mode 100755 index 00000000..265d51fd --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.html @@ -0,0 +1 @@ +

Peerswaps Cancelled

\ No newline at end of file diff --git a/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.scss b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.scss new file mode 100755 index 00000000..e69de29b diff --git a/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.spec.ts b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.spec.ts new file mode 100755 index 00000000..486b81f5 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.spec.ts @@ -0,0 +1,35 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { SharedModule } from '../../../../shared.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PeerswapsCancelledComponent } from './swaps-cancelled.component'; + +describe('PeerswapsCancelledComponent', () => { + let component: PeerswapsCancelledComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PeerswapsCancelledComponent], + imports: [ + BrowserAnimationsModule, + SharedModule + ], + providers: [] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PeerswapsCancelledComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.ts b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.ts new file mode 100755 index 00000000..da1736be --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'rtl-peerswap-cancelled', + templateUrl: './swaps-cancelled.component.html', + styleUrls: ['./swaps-cancelled.component.scss'] +}) +export class PeerswapsCancelledComponent implements OnInit, OnDestroy { + + private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject()]; + + constructor() {} + + ngOnInit() { + } + + ngOnDestroy() { + this.unSubs.forEach((completeSub) => { + completeSub.next(null); + completeSub.complete(); + }); + } + +} diff --git a/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.html b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.html new file mode 100755 index 00000000..b5db1428 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.html @@ -0,0 +1 @@ +

Peerswaps In

\ No newline at end of file diff --git a/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.scss b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.scss new file mode 100755 index 00000000..e69de29b diff --git a/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.spec.ts b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.spec.ts new file mode 100755 index 00000000..c4c9b485 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.spec.ts @@ -0,0 +1,35 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { SharedModule } from '../../../../shared.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PeerswapsInComponent } from './swaps-in.component'; + +describe('PeerswapsInComponent', () => { + let component: PeerswapsInComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PeerswapsInComponent], + imports: [ + BrowserAnimationsModule, + SharedModule + ], + providers: [] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PeerswapsInComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.ts b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.ts new file mode 100755 index 00000000..64ece137 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-in/swaps-in.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'rtl-peer-swaps-in', + templateUrl: './swaps-in.component.html', + styleUrls: ['./swaps-in.component.scss'], +}) +export class PeerswapsInComponent implements OnInit, OnDestroy { + + private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject()]; + + constructor() {} + + ngOnInit() { + } + + ngOnDestroy() { + this.unSubs.forEach((completeSub) => { + completeSub.next(null); + completeSub.complete(); + }); + } + +} diff --git a/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.html b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.html new file mode 100755 index 00000000..c3ce88f3 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.html @@ -0,0 +1 @@ +

Peerswaps Out

\ No newline at end of file diff --git a/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.scss b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.scss new file mode 100755 index 00000000..e69de29b diff --git a/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.spec.ts b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.spec.ts new file mode 100755 index 00000000..f8c54025 --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.spec.ts @@ -0,0 +1,35 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { SharedModule } from '../../../../shared.module'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PeerswapsOutComponent } from './swaps-out.component'; + +describe('PeerswapsOutComponent', () => { + let component: PeerswapsOutComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PeerswapsOutComponent], + imports: [ + BrowserAnimationsModule, + SharedModule + ], + providers: [] + }). + compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PeerswapsOutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + afterEach(() => { + TestBed.resetTestingModule(); + }); +}); diff --git a/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.ts b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.ts new file mode 100755 index 00000000..fa657a0c --- /dev/null +++ b/src/app/shared/components/ln-services/peerswap/swaps-out/swaps-out.component.ts @@ -0,0 +1,25 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; + +@Component({ + selector: 'rtl-peer-swaps-out', + templateUrl: './swaps-out.component.html', + styleUrls: ['./swaps-out.component.scss'], +}) +export class PeerswapsOutComponent implements OnInit, OnDestroy { + + private unSubs: Array> = [new Subject(), new Subject(), new Subject(), new Subject()]; + + constructor() {} + + ngOnInit() { + } + + ngOnDestroy() { + this.unSubs.forEach((completeSub) => { + completeSub.next(null); + completeSub.complete(); + }); + } + +} diff --git a/src/app/shared/components/navigation/side-navigation/side-navigation.component.ts b/src/app/shared/components/navigation/side-navigation/side-navigation.component.ts index 2b3149e2..735838dc 100644 --- a/src/app/shared/components/navigation/side-navigation/side-navigation.component.ts +++ b/src/app/shared/components/navigation/side-navigation/side-navigation.component.ts @@ -35,8 +35,8 @@ export class SideNavigationComponent implements OnInit, OnDestroy { faEye = faEye; public appConfig: RTLConfiguration; public selConfigNodeIndex: Number; - public selNode: ConfigSettingsNode; - public settings: Settings; + public selNode: ConfigSettingsNode | any; + public settings: Settings | null; public version = ''; public information: GetInfoRoot = {}; public informationChain: GetInfoChain = {}; @@ -74,7 +74,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy { this.appConfig = appConfig; }); this.store.select(rootSelNodeAndNodeData).pipe(takeUntil(this.unSubs[1])). - subscribe((rootData: { nodeDate: GetInfoRoot, selNode: ConfigSettingsNode }) => { + subscribe((rootData: { nodeDate: GetInfoRoot, selNode: ConfigSettingsNode | null }) => { this.information = rootData.nodeDate; if (this.information.identity_pubkey) { if (this.information.chains && typeof this.information.chains[0] === 'string') { @@ -94,8 +94,8 @@ export class SideNavigationComponent implements OnInit, OnDestroy { this.smallScreen = true; } this.selNode = rootData.selNode; - this.settings = this.selNode.settings; - this.selConfigNodeIndex = +(rootData.selNode.index || 0); + this.settings = this.selNode?.settings || null; + this.selConfigNodeIndex = +(rootData.selNode?.index || 0); if (this.selNode && this.selNode.lnImplementation) { this.filterSideMenuNodes(); } @@ -143,7 +143,7 @@ export class SideNavigationComponent implements OnInit, OnDestroy { } filterSideMenuNodes() { - switch (this.selNode.lnImplementation?.toUpperCase()) { + switch (this.selNode?.lnImplementation?.toUpperCase()) { case 'CLN': this.loadCLNMenu(); break; @@ -163,12 +163,12 @@ export class SideNavigationComponent implements OnInit, OnDestroy { clonedMenu = JSON.parse(JSON.stringify(MENU_DATA.LNDChildren)); this.navMenus.data = clonedMenu?.filter((navMenuData: any) => { if (navMenuData.children && navMenuData.children.length) { - navMenuData.children = navMenuData.children?.filter((navMenuChild) => ((navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings.userPersona) && navMenuChild.link !== '/services/loop' && navMenuChild.link !== '/services/boltz') || - (navMenuChild.link === '/services/loop' && this.settings.swapServerUrl && this.settings.swapServerUrl.trim() !== '') || - (navMenuChild.link === '/services/boltz' && this.settings.boltzServerUrl && this.settings.boltzServerUrl.trim() !== '')); + navMenuData.children = navMenuData.children?.filter((navMenuChild) => ((navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings?.userPersona) && navMenuChild.link !== '/services/loop' && navMenuChild.link !== '/services/boltz') || + (navMenuChild.link === '/services/loop' && this.settings?.swapServerUrl && this.settings.swapServerUrl.trim() !== '') || + (navMenuChild.link === '/services/boltz' && this.settings?.boltzServerUrl && this.settings.boltzServerUrl.trim() !== '')); return navMenuData.children.length > 0; } - return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona; + return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings?.userPersona; }); } @@ -177,10 +177,11 @@ export class SideNavigationComponent implements OnInit, OnDestroy { clonedMenu = JSON.parse(JSON.stringify(MENU_DATA.CLNChildren)); this.navMenus.data = clonedMenu?.filter((navMenuData: any) => { if (navMenuData.children && navMenuData.children.length) { - navMenuData.children = navMenuData.children?.filter((navMenuChild) => ((navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings.userPersona) && navMenuChild.link !== '/cln/messages') || - (navMenuChild.link === '/cln/messages' && this.information.api_version && this.commonService.isVersionCompatible(this.information.api_version, '0.2.2'))); + navMenuData.children = navMenuData.children?.filter((navMenuChild) => ((navMenuChild.userPersona === UserPersonaEnum.ALL || navMenuChild.userPersona === this.settings?.userPersona) && navMenuChild.link !== '/services/peerswap') || + (navMenuChild.link === '/services/peerswap' && this.settings?.enablePeerswap)); + return navMenuData.children.length > 0; } - return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings.userPersona; + return navMenuData.userPersona === UserPersonaEnum.ALL || navMenuData.userPersona === this.settings?.userPersona; }); } 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 c83942d7..144a564b 100644 --- a/src/app/shared/components/node-config/node-config.component.html +++ b/src/app/shared/components/node-config/node-config.component.html @@ -7,7 +7,7 @@ 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 e0c25eb4..8413a1cb 100644 --- a/src/app/shared/components/node-config/node-config.component.ts +++ b/src/app/shared/components/node-config/node-config.component.ts @@ -18,7 +18,7 @@ export class NodeConfigComponent implements OnInit, OnDestroy { public faTools = faTools; public showLnConfig = false; - public selNode: ConfigSettingsNode; + 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 activeLink = ''; diff --git a/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.html b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.html new file mode 100644 index 00000000..9984b49c --- /dev/null +++ b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.html @@ -0,0 +1,15 @@ +
+
+ + Please ensure that peerswapd is running and accessible to RTL before enabling this service. Click here to learn more about Core Lightning peerswap. +
+
+
+ Enable Peerswap Service +
+
+
+ + +
+
diff --git a/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.scss b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.scss new file mode 100644 index 00000000..15d29fee --- /dev/null +++ b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.scss @@ -0,0 +1,3 @@ +h4 { + word-break: break-word; +} diff --git a/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.ts b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.ts new file mode 100644 index 00000000..e0785bb2 --- /dev/null +++ b/src/app/shared/components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component.ts @@ -0,0 +1,70 @@ +import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { Store } from '@ngrx/store'; + +import { ServicesEnum, UI_MESSAGES } from '../../../../services/consts-enums-functions'; +import { ConfigSettingsNode } from '../../../../models/RTLconfig'; +import { LoggerService } from '../../../../services/logger.service'; +import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; +import { updateServiceSettings } from '../../../../../store/rtl.actions'; +import { RTLState } from '../../../../../store/rtl.state'; +import { setChildNodeSettingsLND } from '../../../../../lnd/store/lnd.actions'; +import { setChildNodeSettingsCL } from '../../../../../cln/store/cln.actions'; +import { setChildNodeSettingsECL } from '../../../../../eclair/store/ecl.actions'; +import { rootSelectedNode } from '../../../../../store/rtl.selector'; + +@Component({ + selector: 'rtl-peerswap-service-settings', + templateUrl: './peerswap-service-settings.component.html', + styleUrls: ['./peerswap-service-settings.component.scss'] +}) +export class PeerswapServiceSettingsComponent implements OnInit, OnDestroy { + + public faInfoCircle = faInfoCircle; + public selNode: ConfigSettingsNode | any; + public enablePeerswap = false; + unSubs: Array> = [new Subject(), new Subject()]; + + constructor(private logger: LoggerService, private store: Store) { } + + ngOnInit() { + this.store.select(rootSelectedNode). + pipe(takeUntil(this.unSubs[0])). + subscribe((selNode) => { + this.selNode = selNode; + this.enablePeerswap = !!selNode?.settings.enablePeerswap; + this.logger.info(selNode); + }); + } + + onUpdateService(): boolean | void { + this.store.dispatch(updateServiceSettings({ payload: { uiMessage: UI_MESSAGES.UPDATE_PEERSWAP_SETTINGS, service: ServicesEnum.PEERSWAP, settings: { enablePeerswap: this.enablePeerswap } } })); + 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, enableOffers: this.selNode.settings.enableOffers, enablePeerswap: this.selNode.settings.enablePeerswap + } + })); + 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, enableOffers: this.selNode.settings.enableOffers, enablePeerswap: this.selNode.settings.enablePeerswap + } + })); + 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, enableOffers: this.selNode.settings.enableOffers, enablePeerswap: this.selNode.settings.enablePeerswap + } + })); + } + + ngOnDestroy() { + this.unSubs.forEach((unsub) => { + unsub.next(); + unsub.complete(); + }); + } + +} diff --git a/src/app/shared/components/node-config/services-settings/services-settings.component.html b/src/app/shared/components/node-config/services-settings/services-settings.component.html index 8a1f6b84..5e99b01d 100644 --- a/src/app/shared/components/node-config/services-settings/services-settings.component.html +++ b/src/app/shared/components/node-config/services-settings/services-settings.component.html @@ -6,8 +6,9 @@
diff --git a/src/app/shared/components/node-config/services-settings/services-settings.component.ts b/src/app/shared/components/node-config/services-settings/services-settings.component.ts index 1f4869d4..d64ee631 100644 --- a/src/app/shared/components/node-config/services-settings/services-settings.component.ts +++ b/src/app/shared/components/node-config/services-settings/services-settings.component.ts @@ -1,8 +1,12 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; -import { Router, ResolveEnd, Event } from '@angular/router'; +import { Router, ResolveEnd, Event, ActivatedRoute } from '@angular/router'; import { Subject } from 'rxjs'; import { takeUntil, filter } from 'rxjs/operators'; import { faLayerGroup } from '@fortawesome/free-solid-svg-icons'; +import { ConfigSettingsNode } from '../../../models/RTLconfig'; +import { Store } from '@ngrx/store'; +import { RTLState } from '../../../../store/rtl.state'; +import { rootSelectedNode } from '../../../../store/rtl.selector'; @Component({ selector: 'rtl-services-settings', @@ -12,11 +16,12 @@ import { faLayerGroup } from '@fortawesome/free-solid-svg-icons'; export class ServicesSettingsComponent implements OnInit, OnDestroy { public faLayerGroup = faLayerGroup; - public links = [{ link: 'loop', name: 'Loop' }, { link: 'boltz', name: 'Boltz' }]; + public links = [{ link: 'loop', name: 'Loop' }, { link: 'boltz', name: 'Boltz' }, { link: 'peerswap', name: 'Peerswap' }]; public activeLink = ''; + public selNode: ConfigSettingsNode | any; private unSubs: Array> = [new Subject(), new Subject(), new Subject()]; - constructor(private router: Router) { } + constructor(private store: Store, private router: Router, private activatedRoute: ActivatedRoute) { } ngOnInit() { const linkFound = this.links.find((link) => this.router.url.includes(link.link)); @@ -25,9 +30,20 @@ export class ServicesSettingsComponent implements OnInit, OnDestroy { subscribe({ next: (value: ResolveEnd | Event) => { const linkFound = this.links.find((link) => (value).urlAfterRedirects.includes(link.link)); - this.activeLink = linkFound ? linkFound.link : this.links[0].link; + if(this.selNode.lnImplementation.toUpperCase() === 'CLN') { + this.activeLink = this.links[2].link; + } else { + this.activeLink = linkFound ? linkFound.link : this.links[0].link; + } } }); + this.store.select(rootSelectedNode).pipe(takeUntil(this.unSubs[1])).subscribe((selNode) => { + this.selNode = selNode; + if (this.selNode.lnImplementation.toUpperCase() === 'CLN') { + this.activeLink = this.links[2].link; + this.router.navigate(['./' + this.activeLink], { relativeTo: this.activatedRoute }); + } + }); } ngOnDestroy() { diff --git a/src/app/shared/models/RTLconfig.ts b/src/app/shared/models/RTLconfig.ts index c9b0a5d1..b49bf191 100644 --- a/src/app/shared/models/RTLconfig.ts +++ b/src/app/shared/models/RTLconfig.ts @@ -24,7 +24,8 @@ export class Settings { public boltzServerUrl?: string, public channelBackupPath?: string, public currencyUnit?: string, - public enableOffers?: boolean + public enableOffers?: boolean, + public enablePeerswap?: boolean ) { } } @@ -84,6 +85,7 @@ export interface SelNodeChild { swapServerUrl?: string; boltzServerUrl?: string; enableOffers?: boolean; + enablePeerswap?: boolean; } export class HelpTopic { diff --git a/src/app/shared/models/navMenu.ts b/src/app/shared/models/navMenu.ts index a9fa5e9c..ab28b0d9 100644 --- a/src/app/shared/models/navMenu.ts +++ b/src/app/shared/models/navMenu.ts @@ -1,4 +1,4 @@ -import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faChartBar, faTools, faProjectDiagram, faDownload, faServer, faPercentage, faInfinity, faUserCheck, faLayerGroup, faBullhorn } from '@fortawesome/free-solid-svg-icons'; +import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faChartBar, faTools, faProjectDiagram, faDownload, faServer, faPercentage, faInfinity, faUserCheck, faLayerGroup, faBullhorn, faHandshake } from '@fortawesome/free-solid-svg-icons'; import { UserPersonaEnum } from '../services/consts-enums-functions'; export class MenuChildNode { @@ -64,8 +64,13 @@ export const MENU_DATA: MenuRootNode = { { id: 39, parentId: 3, name: 'Node/Fee Rates', iconType: 'FA', icon: faServer, link: '/cln/rates', userPersona: UserPersonaEnum.MERCHANT } ] }, - { id: 4, parentId: 0, name: 'Node Config', iconType: 'FA', icon: faTools, link: '/config', userPersona: UserPersonaEnum.ALL }, - { id: 5, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL } + { + id: 4, parentId: 0, name: 'Services', iconType: 'FA', icon: faLayerGroup, link: '/services/peerswap', userPersona: UserPersonaEnum.ALL, children: [ + { id: 41, parentId: 4, name: 'Peerswap', iconType: 'FA', icon: faHandshake, link: '/services/peerswap', userPersona: UserPersonaEnum.ALL }, + ] + }, + { id: 5, parentId: 0, name: 'Node Config', iconType: 'FA', icon: faTools, link: '/config', userPersona: UserPersonaEnum.ALL }, + { id: 6, parentId: 0, name: 'Help', iconType: 'FA', icon: faQuestion, link: '/help', userPersona: UserPersonaEnum.ALL } ], ECLChildren: [ { id: 1, parentId: 0, name: 'Dashboard', iconType: 'FA', icon: faTachometerAlt, link: '/ecl/home', userPersona: UserPersonaEnum.ALL }, diff --git a/src/app/shared/services/consts-enums-functions.ts b/src/app/shared/services/consts-enums-functions.ts index 2248512d..08d8b07a 100644 --- a/src/app/shared/services/consts-enums-functions.ts +++ b/src/app/shared/services/consts-enums-functions.ts @@ -243,7 +243,8 @@ export const SCROLL_RANGES = ['MONTHLY', 'YEARLY']; export enum ServicesEnum { LOOP = 'LOOP', BOLTZ = 'BOLTZ', - OFFERS = 'OFFERS' + OFFERS = 'OFFERS', + PEERSWAP = 'PEERSWAP' } export const PASSWORD_BLACKLIST = ['password', 'changeme', 'moneyprintergobrrr']; @@ -296,6 +297,7 @@ export const UI_MESSAGES = { WAIT_SYNC_NODE: 'Waiting for Node Sync...', UPDATE_BOLTZ_SETTINGS: 'Updating Boltz Service Settings...', UPDATE_LOOP_SETTINGS: 'Updating Loop Service Settings...', + UPDATE_PEERSWAP_SETTINGS: 'Updating Peerswap Service Settings...', UPDATE_SETTING: 'Updating Setting...', UPDATE_UI_SETTINGS: 'Updating Settings...', UPDATE_NODE_SETTINGS: 'Updating Node Settings...', diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index 01838410..da0d1a41 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -65,6 +65,7 @@ import { NodeSettingsComponent } from './components/node-config/node-settings/no 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'; +import { PeerswapServiceSettingsComponent } from './components/node-config/services-settings/peerswap-service-settings/peerswap-service-settings.component'; import { ExperimentalSettingsComponent } from './components/node-config/experimental-settings/experimental-settings.component'; import { ErrorComponent } from './components/error/error.component'; import { CurrencyUnitConverterComponent } from './components/currency-unit-converter/currency-unit-converter.component'; @@ -79,21 +80,26 @@ import { ErrorMessageComponent } from './components/data-modal/error-message/err import { TwoFactorAuthComponent } from './components/data-modal/two-factor-auth/two-factor-auth.component'; import { LoginTokenComponent } from './components/data-modal/login-2fa-token/login-2fa-token.component'; -import { ServicesComponent } from './components/services/services.component'; -import { LoopComponent } from '../shared/components/services/loop/loop.component'; -import { SwapsComponent } from '../shared/components/services/loop/swaps/swaps.component'; -import { LoopModalComponent } from '../shared/components/services/loop/loop-modal/loop-modal.component'; -import { LoopQuoteComponent } from '../shared/components/services/loop/loop-quote/loop-quote.component'; -import { LoopStatusComponent } from '../shared/components/services/loop/loop-status/loop-status.component'; -import { LoopOutInfoGraphicsComponent } from '../shared/components/services/loop/loop-out-info-graphics/info-graphics.component'; -import { LoopInInfoGraphicsComponent } from '../shared/components/services/loop/loop-in-info-graphics/info-graphics.component'; -import { BoltzRootComponent } from './components/services/boltz/boltz-root.component'; -import { BoltzSwapsComponent } from './components/services/boltz/swaps/swaps.component'; -import { SwapStatusComponent } from './components/services/boltz/swap-status/swap-status.component'; -import { SwapServiceInfoComponent } from './components/services/boltz/swap-service-info/swap-service-info.component'; -import { SwapModalComponent } from './components/services/boltz/swap-modal/swap-modal.component'; -import { SwapInInfoGraphicsComponent } from './components/services/boltz/swap-in-info-graphics/info-graphics.component'; -import { SwapOutInfoGraphicsComponent } from './components/services/boltz/swap-out-info-graphics/info-graphics.component'; +import { LNServicesComponent } from './components/ln-services/ln-services.component'; +import { LoopComponent } from '../shared/components/ln-services/loop/loop.component'; +import { SwapsComponent } from '../shared/components/ln-services/loop/swaps/swaps.component'; +import { LoopModalComponent } from '../shared/components/ln-services/loop/loop-modal/loop-modal.component'; +import { LoopQuoteComponent } from '../shared/components/ln-services/loop/loop-quote/loop-quote.component'; +import { LoopStatusComponent } from '../shared/components/ln-services/loop/loop-status/loop-status.component'; +import { LoopOutInfoGraphicsComponent } from '../shared/components/ln-services/loop/loop-out-info-graphics/info-graphics.component'; +import { LoopInInfoGraphicsComponent } from '../shared/components/ln-services/loop/loop-in-info-graphics/info-graphics.component'; +import { BoltzRootComponent } from './components/ln-services/boltz/boltz-root.component'; +import { BoltzSwapsComponent } from './components/ln-services/boltz/swaps/swaps.component'; +import { SwapStatusComponent } from './components/ln-services/boltz/swap-status/swap-status.component'; +import { SwapServiceInfoComponent } from './components/ln-services/boltz/swap-service-info/swap-service-info.component'; +import { SwapModalComponent } from './components/ln-services/boltz/swap-modal/swap-modal.component'; +import { SwapInInfoGraphicsComponent } from './components/ln-services/boltz/swap-in-info-graphics/info-graphics.component'; +import { SwapOutInfoGraphicsComponent } from './components/ln-services/boltz/swap-out-info-graphics/info-graphics.component'; +import { PeerswapComponent } from './components/ln-services/peerswap/peerswap.component'; +import { SwapPeersComponent } from './components/ln-services/peerswap/swap-peers/swap-peers.component'; +import { PeerswapsCancelledComponent } from './components/ln-services/peerswap/swaps-cancelled/swaps-cancelled.component'; +import { PeerswapsInComponent } from './components/ln-services/peerswap/swaps-in/swaps-in.component'; +import { PeerswapsOutComponent } from './components/ln-services/peerswap/swaps-out/swaps-out.component'; import { ClipboardDirective } from './directive/clipboard.directive'; import { AutoFocusDirective } from './directive/auto-focus.directive'; @@ -244,11 +250,12 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { ServicesSettingsComponent, LoopServiceSettingsComponent, BoltzServiceSettingsComponent, + PeerswapServiceSettingsComponent, ExperimentalSettingsComponent, CurrencyUnitConverterComponent, HorizontalScrollerComponent, TransactionsReportTableComponent, - ServicesComponent, + LNServicesComponent, LoopComponent, SwapsComponent, LoopModalComponent, @@ -262,7 +269,12 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { SwapServiceInfoComponent, SwapModalComponent, SwapInInfoGraphicsComponent, - SwapOutInfoGraphicsComponent + SwapOutInfoGraphicsComponent, + PeerswapComponent, + SwapPeersComponent, + PeerswapsCancelledComponent, + PeerswapsInComponent, + PeerswapsOutComponent ], declarations: [ AppSettingsComponent, @@ -282,6 +294,7 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { ServicesSettingsComponent, LoopServiceSettingsComponent, BoltzServiceSettingsComponent, + PeerswapServiceSettingsComponent, ExperimentalSettingsComponent, CurrencyUnitConverterComponent, HorizontalScrollerComponent, @@ -305,7 +318,7 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { TwoFactorAuthComponent, LoginTokenComponent, TransactionsReportTableComponent, - ServicesComponent, + LNServicesComponent, LoopComponent, SwapsComponent, LoopModalComponent, @@ -319,7 +332,12 @@ export const DEFAULT_DATE_FORMAT: MatDateFormats = { SwapServiceInfoComponent, SwapModalComponent, SwapInInfoGraphicsComponent, - SwapOutInfoGraphicsComponent + SwapOutInfoGraphicsComponent, + PeerswapComponent, + SwapPeersComponent, + PeerswapsCancelledComponent, + PeerswapsInComponent, + PeerswapsOutComponent ], providers: [ { provide: LoggerService, useClass: ConsoleLoggerService }, diff --git a/src/app/shared/test-helpers/test-data.ts b/src/app/shared/test-helpers/test-data.ts index fc2b8ae1..c13c0314 100644 --- a/src/app/shared/test-helpers/test-data.ts +++ b/src/app/shared/test-helpers/test-data.ts @@ -759,7 +759,8 @@ export const mockActionsData = { boltzServerUrl: '', channelBackupPath: '', currencyUnit: '', - enableOffers: false + enableOffers: false, + enablePeerswap: false }, authentication: { swapMacaroonPath: '', @@ -782,7 +783,8 @@ export const mockActionsData = { lnImplementation: 'LND', swapServerUrl: '', boltzServerUrl: '', - enableOffers: false + enableOffers: false, + enablePeerswap: false }, setSelectedNode: { settings: { @@ -802,7 +804,8 @@ export const mockActionsData = { boltzServerUrl: '', channelBackupPath: '', currencyUnit: '', - enableOffers: false + enableOffers: false, + enablePeerswap: false }, authentication: { swapMacaroonPath: '', diff --git a/src/app/store/rtl.effects.ts b/src/app/store/rtl.effects.ts index 80c06b9e..a6bd0a41 100644 --- a/src/app/store/rtl.effects.ts +++ b/src/app/store/rtl.effects.ts @@ -499,12 +499,12 @@ export class RTLEffects implements OnDestroy { mergeMap((action: { type: string, payload: SetSelectedNode }) => { this.store.dispatch(openSpinner({ payload: action.payload.uiMessage })); this.store.dispatch(updateRootAPICallStatus({ payload: { action: 'UpdateSelNode', status: APICallStatusEnum.INITIATED } })); - return this.httpClient.get(environment.CONF_API + '/updateSelNode/' + action.payload.currentLnNode.index + '/' + action.payload.prevLnNodeIndex).pipe( + return this.httpClient.get(environment.CONF_API + '/updateSelNode/' + action.payload.currentLnNode?.index + '/' + action.payload.prevLnNodeIndex).pipe( map((postRes: any) => { this.logger.info(postRes); this.store.dispatch(updateRootAPICallStatus({ payload: { action: 'UpdateSelNode', status: APICallStatusEnum.COMPLETED } })); this.store.dispatch(closeSpinner({ payload: action.payload.uiMessage })); - this.initializeNode(action.payload.currentLnNode, action.payload.isInitialSetup); + this.initializeNode(action.payload.currentLnNode!, action.payload.isInitialSetup); return { type: RTLActions.VOID }; }), catchError((err: any) => { @@ -551,9 +551,9 @@ export class RTLEffects implements OnDestroy { const landingPage = isInitialSetup ? '' : 'HOME'; let selNode = {}; if (node.settings.fiatConversion && node.settings.currencyUnit) { - selNode = { userPersona: node.settings.userPersona, channelBackupPath: node.settings.channelBackupPath, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: [...CURRENCY_UNITS, node.settings.currencyUnit], fiatConversion: node.settings.fiatConversion, lnImplementation: node.lnImplementation, swapServerUrl: node.settings.swapServerUrl, boltzServerUrl: node.settings.boltzServerUrl, enableOffers: node.settings.enableOffers }; + selNode = { userPersona: node.settings.userPersona, channelBackupPath: node.settings.channelBackupPath, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: [...CURRENCY_UNITS, node.settings.currencyUnit], fiatConversion: node.settings.fiatConversion, lnImplementation: node.lnImplementation, swapServerUrl: node.settings.swapServerUrl, boltzServerUrl: node.settings.boltzServerUrl, enableOffers: node.settings.enableOffers, enablePeerswap: node.settings.enablePeerswap }; } else { - selNode = { userPersona: node.settings.userPersona, channelBackupPath: node.settings.channelBackupPath, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: CURRENCY_UNITS, fiatConversion: node.settings.fiatConversion, lnImplementation: node.lnImplementation, swapServerUrl: node.settings.swapServerUrl, boltzServerUrl: node.settings.boltzServerUrl, enableOffers: node.settings.enableOffers }; + selNode = { userPersona: node.settings.userPersona, channelBackupPath: node.settings.channelBackupPath, selCurrencyUnit: node.settings.currencyUnit, currencyUnits: CURRENCY_UNITS, fiatConversion: node.settings.fiatConversion, lnImplementation: node.lnImplementation, swapServerUrl: node.settings.swapServerUrl, boltzServerUrl: node.settings.boltzServerUrl, enableOffers: node.settings.enableOffers, enablePeerswap: node.settings.enablePeerswap }; } this.sessionService.removeItem('lndUnlocked'); this.sessionService.removeItem('clUnlocked'); diff --git a/src/app/store/rtl.reducers.ts b/src/app/store/rtl.reducers.ts index 3a58c927..20d94fc5 100644 --- a/src/app/store/rtl.reducers.ts +++ b/src/app/store/rtl.reducers.ts @@ -44,6 +44,9 @@ export const RootReducer = createReducer(initRootState, case ServicesEnum.OFFERS: updatedSelNode.settings.enableOffers = payload.settings.enableOffers; break; + case ServicesEnum.PEERSWAP: + updatedSelNode.settings.enablePeerswap = payload.settings.enablePeerswap; + break; default: break; diff --git a/src/app/store/rtl.state.ts b/src/app/store/rtl.state.ts index 7260e88d..ea9f23aa 100644 --- a/src/app/store/rtl.state.ts +++ b/src/app/store/rtl.state.ts @@ -9,12 +9,12 @@ import { ECLState } from '../eclair/store/ecl.state'; export interface RootState { apiURL: string; apisCallStatus: ApiCallsListRoot; - selNode: ConfigSettingsNode | null; + selNode: ConfigSettingsNode | any; appConfig: RTLConfiguration; nodeData: GetInfoRoot; } -const initNodeSettings = { userPersona: 'OPERATOR', themeMode: 'DAY', themeColor: 'PURPLE', channelBackupPath: '', selCurrencyUnit: 'USD', fiatConversion: false, currencyUnits: ['Sats', 'BTC', 'USD'], bitcoindConfigPath: '', enableOffers: false }; +const initNodeSettings = { userPersona: 'OPERATOR', themeMode: 'DAY', themeColor: 'PURPLE', channelBackupPath: '', selCurrencyUnit: 'USD', fiatConversion: false, currencyUnits: ['Sats', 'BTC', 'USD'], bitcoindConfigPath: '', enableOffers: false, enablePeerswap: false }; const initNodeAuthentication = { configPath: '', swapMacaroonPath: '', boltzMacaroonPath: '' }; export const initRootState: RootState = {