parent
3795851acf
commit
9da3eed1b6
@ -0,0 +1,93 @@
|
||||
<div fxLayout="row">
|
||||
<div fxFlex="100">
|
||||
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
|
||||
<div fxFlex="95" fxLayoutAlign="start start">
|
||||
<span class="page-title">{{alertTitle}}</span>
|
||||
</div>
|
||||
<button tabindex="6" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content class="padding-gap-x-large">
|
||||
<form fxLayout="column" #form="ngForm">
|
||||
<ng-container *ngTemplateOutlet="nodeDetailsExpansionBlock"></ng-container>
|
||||
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap">
|
||||
<mat-form-field fxFlex="30" fxLayoutAlign="start end">
|
||||
<input autoFocus matInput [(ngModel)]="requestedAmount" placeholder="Requested Amount" type="number" [step]="1000" [min]="0" tabindex="1" required name="ramount" #ramount="ngModel">
|
||||
<span matSuffix> Sats </span>
|
||||
<mat-error *ngIf="ramount.errors?.required">Requested amount is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="30" fxLayoutAlign="start end">
|
||||
<input matInput [(ngModel)]="feeRate" placeholder="Fee Rate" type="number" [step]="1" [min]="0" tabindex="2" required name="feerate" #feeRt="ngModel">
|
||||
<span matSuffix> Sats/vByte </span>
|
||||
<mat-error *ngIf="feeRt.errors?.required">Fee rate is required.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxFlex="30" fxLayoutAlign="start end">
|
||||
<input matInput [(ngModel)]="localAmount" placeholder="Local Amount" type="number" [step]="1000" [min]="20000" [max]="totalBalance" tabindex="3" required name="lamount" #lamount="ngModel">
|
||||
<mat-hint>Remaining Bal: {{totalBalance - ((localAmount) ? localAmount : 0) | number}}</mat-hint>
|
||||
<span matSuffix> Sats </span>
|
||||
<mat-error *ngIf="lamount.errors?.required">Local amount is required.</mat-error>
|
||||
<mat-error *ngIf="lamount.errors?.min">Local amount must be greater than or equal to 20000.</mat-error>
|
||||
<mat-error *ngIf="lamount.errors?.max">Local amount must be less than or equal to {{totalBalance}}.</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div fxFlex="100" class="alert alert-info mt-2">
|
||||
<span>Total cost to lease {{1250 | number}} (Sats)</span>
|
||||
</div>
|
||||
<div fxFlex="100" class="alert alert-danger mt-2" *ngIf="channelConnectionError !== ''">
|
||||
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
|
||||
<span *ngIf="channelConnectionError !== ''">{{channelConnectionError}}</span>
|
||||
</div>
|
||||
<div class="mt-2" fxLayout="row" fxLayoutAlign="end center">
|
||||
<button mat-button color="primary" class="mr-1" tabindex="4" (click)="resetData()">Clear</button>
|
||||
<button autoFocus mat-button color="primary" tabindex="5" (click)="onOpenChannel()">Execute</button>
|
||||
</div>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #nodeDetailsExpansionBlock>
|
||||
<mat-expansion-panel class="flat-expansion-panel my-1" *ngIf="node" expanded="false">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>
|
||||
<span>Node: </span><strong class="font-weight-900">{{node?.alias || node?.nodeid}}</strong>
|
||||
</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div fxLayout="column">
|
||||
<div fxLayout="row">
|
||||
<div fxFlex="100">
|
||||
<h4 fxLayoutAlign="start" class="font-bold-500">Pubkey</h4>
|
||||
<span class="foreground-secondary-text">{{node.nodeid}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="w-100 my-1"></mat-divider>
|
||||
<div fxLayout="row">
|
||||
<div fxFlex="100">
|
||||
<h4 fxLayoutAlign="start" class="font-bold-500">Last Timestamp</h4>
|
||||
<span class="overflow-wrap foreground-secondary-text">{{node.last_timestamp}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<mat-divider class="w-100 my-1"></mat-divider>
|
||||
<div fxLayout="column" fxLayoutAlign="start stretch">
|
||||
<h4 fxFlex="100" class="font-bold-500 mb-1">Addresses</h4>
|
||||
<div class="table-container">
|
||||
<table mat-table #table [dataSource]="node.addresses" matSort class="overflow-auto">
|
||||
<ng-container matColumnDef="type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Type</th>
|
||||
<td mat-cell *matCellDef="let address"> {{address?.type}} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="address">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Address</th>
|
||||
<td mat-cell *matCellDef="let address"> {{address?.address }} </td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="port">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Port</th>
|
||||
<td mat-cell *matCellDef="let address"> {{address?.port}} </td>
|
||||
</ng-container>
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns;"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</ng-template>
|
@ -0,0 +1,3 @@
|
||||
.open-inputs-box {
|
||||
padding: 1.2rem 2.4rem 0.8rem 2.4rem !important;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
|
||||
import { CommonService } from '../../../shared/services/common.service';
|
||||
import { DataService } from '../../../shared/services/data.service';
|
||||
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 { CLNOpenLiquidityChannelComponent } from './open-liquidity-channel-modal.component';
|
||||
import { mockCLEffects, mockDataService, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../shared/test-helpers/mock-services';
|
||||
import { SharedModule } from '../../../shared/shared.module';
|
||||
|
||||
describe('CLNOpenLiquidityChannelComponent', () => {
|
||||
let component: CLNOpenLiquidityChannelComponent;
|
||||
let fixture: ComponentFixture<CLNOpenLiquidityChannelComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CLNOpenLiquidityChannelComponent],
|
||||
imports: [
|
||||
BrowserAnimationsModule,
|
||||
SharedModule,
|
||||
StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cln: CLNReducer, ecl: ECLReducer }),
|
||||
EffectsModule.forRoot([mockRTLEffects, mockLNDEffects, mockCLEffects, mockECLEffects])
|
||||
],
|
||||
providers: [
|
||||
CommonService,
|
||||
{ provide: DataService, useClass: mockDataService },
|
||||
{ provide: MatDialogRef, useClass: mockMatDialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: { message: {} } }
|
||||
]
|
||||
}).
|
||||
compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(CLNOpenLiquidityChannelComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
});
|
@ -0,0 +1,84 @@
|
||||
import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil, filter } from 'rxjs/operators';
|
||||
import { Actions } from '@ngrx/effects';
|
||||
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import { LookupNode } from '../../../shared/models/clnModels';
|
||||
import { CLNOpenLiquidityChannelAlert } from '../../../shared/models/alertData';
|
||||
import { APICallStatusEnum, CLNActions } from '../../../shared/services/consts-enums-functions';
|
||||
|
||||
import { RTLState } from '../../../store/rtl.state';
|
||||
import { saveNewChannel } from '../../store/cln.actions';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-cln-open-liquidity-channel',
|
||||
templateUrl: './open-liquidity-channel-modal.component.html',
|
||||
styleUrls: ['./open-liquidity-channel-modal.component.scss']
|
||||
})
|
||||
export class CLNOpenLiquidityChannelComponent implements OnInit, OnDestroy {
|
||||
|
||||
public faExclamationTriangle = faExclamationTriangle;
|
||||
@ViewChild('form', { static: true }) form: any;
|
||||
public alertTitle: string;
|
||||
public totalBalance = 0;
|
||||
public node: LookupNode;
|
||||
public requestedAmount = 0;
|
||||
public feeRate = 0;
|
||||
public localAmount = 0;
|
||||
public channelConnectionError = '';
|
||||
public displayedColumns = ['type', 'address', 'port'];
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
|
||||
|
||||
constructor(public dialogRef: MatDialogRef<CLNOpenLiquidityChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLNOpenLiquidityChannelAlert, private actions: Actions, private store: Store<RTLState>) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.alertTitle = this.data.alertTitle;
|
||||
this.totalBalance = this.data.message.balance;
|
||||
this.node = this.data.message.node;
|
||||
this.requestedAmount = this.data.message.requestedAmount;
|
||||
this.feeRate = this.data.message.feeRate;
|
||||
this.localAmount = this.data.message.localAmount;
|
||||
this.actions.pipe(
|
||||
takeUntil(this.unSubs[0]),
|
||||
filter((action) => action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN || action.type === CLNActions.FETCH_CHANNELS_CLN)).
|
||||
subscribe((action: any) => {
|
||||
if (action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN && action.payload.status === APICallStatusEnum.ERROR && action.payload.action === 'SaveNewChannel') {
|
||||
this.channelConnectionError = action.payload.message;
|
||||
}
|
||||
if (action.type === CLNActions.FETCH_CHANNELS_CLN) {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
|
||||
resetData() {
|
||||
this.form.resetForm();
|
||||
this.form.controls.ramount.setValue(this.data.message.requestedAmount);
|
||||
this.form.controls.feerate.setValue(this.data.message.feeRate);
|
||||
this.form.controls.lamount.setValue(this.data.message.localAmount);
|
||||
this.channelConnectionError = '';
|
||||
}
|
||||
|
||||
onOpenChannel(): boolean | void {
|
||||
if (!this.node || !this.requestedAmount || !this.feeRate || !this.localAmount) {
|
||||
return true;
|
||||
}
|
||||
const newChannel = { peerId: this.node.nodeid, satoshis: this.requestedAmount.toString(), announce: false, feeRate: this.feeRate + 'perkb' };
|
||||
this.store.dispatch(saveNewChannel({ payload: newChannel }));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach((completeSub) => {
|
||||
completeSub.next(null);
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue