Liquidity Ads List Display Incomplete
Liquidity Ads List Display Incompletepull/1032/head
parent
47011011d9
commit
346e414181
@ -1,10 +1,11 @@
|
|||||||
import exprs from 'express';
|
import exprs from 'express';
|
||||||
const { Router } = exprs;
|
const { Router } = exprs;
|
||||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||||
import { getRoute, listNode, listChannel, feeRates } from '../../controllers/cln/network.js';
|
import { getRoute, listNode, listChannel, feeRates, listNodes } from '../../controllers/cln/network.js';
|
||||||
const router = Router();
|
const router = Router();
|
||||||
router.get('/getRoute/:destPubkey/:amount', isAuthenticated, getRoute);
|
router.get('/getRoute/:destPubkey/:amount', isAuthenticated, getRoute);
|
||||||
router.get('/listNode/:id', isAuthenticated, listNode);
|
router.get('/listNode/:id', isAuthenticated, listNode);
|
||||||
router.get('/listChannel/:channelShortId', isAuthenticated, listChannel);
|
router.get('/listChannel/:channelShortId', isAuthenticated, listChannel);
|
||||||
router.get('/feeRates/:feeRateStyle', isAuthenticated, feeRates);
|
router.get('/feeRates/:feeRateStyle', isAuthenticated, feeRates);
|
||||||
|
router.get('/listNodes', isAuthenticated, listNodes);
|
||||||
export default router;
|
export default router;
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
|
||||||
|
<fa-icon [icon]="faWater" class="page-title-img mr-1"></fa-icon>
|
||||||
|
<span class="page-title">Liquidity Ads</span>
|
||||||
|
</div>
|
||||||
|
<div fxLayout="column" class="padding-gap-x">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-content class="padding-gap-large">
|
||||||
|
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
|
||||||
|
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #formAsk="ngForm">
|
||||||
|
<div fxFlex="100" fxLayout="row" class="alert alert-warn">
|
||||||
|
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
|
||||||
|
<span>Ads should be suplemented with additional research of the nodes, before buying liquidity.</span>
|
||||||
|
</div>
|
||||||
|
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start start" class="page-sub-title-container mt-1">
|
||||||
|
<div fxFlex="30">
|
||||||
|
<span class="page-title">
|
||||||
|
Liquidity Ask
|
||||||
|
<mat-icon matTooltip="Tooltip for Liquidity Ask" matTooltipPosition="above" class="info-icon info-icon-primary">info_outline</mat-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<mat-form-field fxFlex="34">
|
||||||
|
<input autoFocus matInput placeholder="Channel Amount (Sats)" name="channelAmount" [(ngModel)]="channelAmount" tabindex="1" type="number" step="1000" required>
|
||||||
|
<mat-error *ngIf="!channelAmount">Channel amount is required.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field fxFlex="34">
|
||||||
|
<input matInput placeholder="Channel Opening Fee Rate (Sats/vByte)" name="channelOpeningFeeRate" [(ngModel)]="channelOpeningFeeRate" type="number" step="1" tabindex="2" required>
|
||||||
|
<mat-error *ngIf="!channelOpeningFeeRate">Channel opening fee rate is required.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div fxLayout="row" class="my-1">
|
||||||
|
<button class="mr-1" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="onReset()">Clear</button>
|
||||||
|
<button mat-flat-button color="primary" (click)="onRecalculate()" tabindex="4" type="submit">Recalculate</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<mat-divider [inset]="true" class="my-2"></mat-divider>
|
||||||
|
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #formFilter="ngForm">
|
||||||
|
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start start" class="page-sub-title-container mt-1">
|
||||||
|
<div fxFlex="30">
|
||||||
|
<span class="page-title">
|
||||||
|
Nodes Advertising Liquidity
|
||||||
|
<mat-icon matTooltip="Tooltip for Nodes Advertising Liquidity" matTooltipPosition="above" class="info-icon info-icon-primary">info_outline</mat-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<mat-form-field fxFlex="34">
|
||||||
|
<input matInput placeholder="Node Capacity (Sats)" name="nodeCapacity" [(ngModel)]="nodeCapacity" tabindex="5" type="number" step="1000" required>
|
||||||
|
<mat-error *ngIf="!nodeCapacity">Node capacity is required.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field fxFlex="34">
|
||||||
|
<input matInput placeholder="Channel Count" name="channelCount" [(ngModel)]="channelCount" type="number" step="1" tabindex="6" required>
|
||||||
|
<mat-error *ngIf="!channelCount">Channel count is required.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div fxLayout="row" class="my-1">
|
||||||
|
<button class="mr-1" mat-stroked-button color="primary" tabindex="7" type="reset" (click)="onFilterReset()">Clear</button>
|
||||||
|
<button mat-flat-button color="primary" (click)="onFilter()" tabindex="8" type="submit">Filter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div [perfectScrollbar] fxLayout="column" fxLayoutAlign="start center" fxFlex="100" class="table-container">
|
||||||
|
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
|
||||||
|
<table mat-table #table [dataSource]="liquidityNodes" matSort [ngClass]="{'overflow-auto error-border': errorMessage !== '','overflow-auto': true}">
|
||||||
|
<ng-container matColumnDef="alias">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header> Alias </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '40rem'}">
|
||||||
|
{{lqNode?.alias}}
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="capacity">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Capacity </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode"><span fxLayoutAlign="end center">
|
||||||
|
{{lqNode?.option_will_fund?.lease_fee_basis | number:'1.0-0'}} </span></td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="numChannels">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Channels </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode"><span fxLayoutAlign="end center">
|
||||||
|
{{lqNode?.option_will_fund?.lease_fee_basis | number:'1.0-0'}} </span></td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="leaseFeeBasis">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Lease Fee </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode"><span fxLayoutAlign="end center">
|
||||||
|
{{lqNode?.option_will_fund?.lease_fee_basis | number:'1.0-0'}} </span></td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="routingFee">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Routing Fee </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode"><span fxLayoutAlign="end center">
|
||||||
|
{{lqNode?.option_will_fund?.lease_fee_basis | number:'1.0-0'}} </span></td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="channelOpenFee">
|
||||||
|
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before"> Channel Open Fee </th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode"><span fxLayoutAlign="end center">
|
||||||
|
{{lqNode?.option_will_fund?.lease_fee_basis | number:'1.0-0'}} </span></td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="actions">
|
||||||
|
<th mat-header-cell *matHeaderCellDef class="px-3">
|
||||||
|
<div class="bordered-box table-actions-select">
|
||||||
|
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
|
||||||
|
<mat-select-trigger></mat-select-trigger>
|
||||||
|
<mat-option (click)="onDownloadCSV()">Download CSV</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<td mat-cell *matCellDef="let lqNode" fxLayoutAlign="end center" class="px-3">
|
||||||
|
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">
|
||||||
|
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
|
||||||
|
<mat-select-trigger></mat-select-trigger>
|
||||||
|
<mat-option (click)="onNodeClick(lqNode)">View Info</mat-option>
|
||||||
|
<mat-option (click)="onOpenChannel(lqNode)">Open Channel</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container matColumnDef="no_lqNode">
|
||||||
|
<td mat-footer-cell *matFooterCellDef colspan="4">
|
||||||
|
<p *ngIf="(!liquidityNodes?.data || liquidityNodes?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.COMPLETED">No node with liquidity.</p>
|
||||||
|
<p *ngIf="(!liquidityNodes?.data || liquidityNodes?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.INITIATED">Getting nodes with liquidity...</p>
|
||||||
|
<p *ngIf="(!liquidityNodes?.data || liquidityNodes?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.ERROR">{{errorMessage}}</p>
|
||||||
|
</td>
|
||||||
|
</ng-container>
|
||||||
|
<tr mat-footer-row *matFooterRowDef="['no_lqNode']" [ngClass]="{'display-none': liquidityNodes?.data && liquidityNodes?.data?.length>0}"></tr>
|
||||||
|
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
|
||||||
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" class="mb-1"></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
@ -0,0 +1,10 @@
|
|||||||
|
.mat-column-alias {
|
||||||
|
flex: 1 1 20%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-column-actions {
|
||||||
|
min-height: 4.8rem;
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
|
||||||
|
import { DataService } from '../../shared/services/data.service';
|
||||||
|
import { CommonService } from '../../shared/services/common.service';
|
||||||
|
import { mockDataService } from '../../shared/test-helpers/mock-services';
|
||||||
|
|
||||||
|
import { CLNLiquidityAdsComponent } from './liquidity-ads.component';
|
||||||
|
|
||||||
|
describe('CLNLiquidityAdsComponent', () => {
|
||||||
|
let component: CLNLiquidityAdsComponent;
|
||||||
|
let fixture: ComponentFixture<CLNLiquidityAdsComponent>;
|
||||||
|
|
||||||
|
beforeEach(waitForAsync(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [CLNLiquidityAdsComponent],
|
||||||
|
imports: [
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
SharedModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CommonService,
|
||||||
|
{ provide: DataService, useClass: mockDataService }
|
||||||
|
]
|
||||||
|
}).
|
||||||
|
compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CLNLiquidityAdsComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,128 @@
|
|||||||
|
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { MatTableDataSource } from '@angular/material/table';
|
||||||
|
import { MatSort } from '@angular/material/sort';
|
||||||
|
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
|
||||||
|
import { faWater, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
|
||||||
|
import { DataService } from '../../shared/services/data.service';
|
||||||
|
import { LoggerService } from '../../shared/services/logger.service';
|
||||||
|
import { CommonService } from '../../shared/services/common.service';
|
||||||
|
import { APICallStatusEnum, getPaginatorLabel, PAGE_SIZE, PAGE_SIZE_OPTIONS, ScreenSizeEnum } from '../../shared/services/consts-enums-functions';
|
||||||
|
import { LookupNode } from '../../shared/models/clnModels';
|
||||||
|
import { ApiCallStatusPayload } from '../../shared/models/apiCallsPayload';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'rtl-cln-liquidity-ads',
|
||||||
|
templateUrl: './liquidity-ads.component.html',
|
||||||
|
styleUrls: ['./liquidity-ads.component.scss'],
|
||||||
|
providers: [
|
||||||
|
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('Liquidity Ads') }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CLNLiquidityAdsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild(MatSort, { static: false }) sort: MatSort | undefined;
|
||||||
|
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator | undefined;
|
||||||
|
public displayedColumns: any[] = [];
|
||||||
|
public faWater = faWater;
|
||||||
|
public faExclamationTriangle = faExclamationTriangle;
|
||||||
|
public channelAmount = 0;
|
||||||
|
public channelOpeningFeeRate = 0;
|
||||||
|
public nodeCapacity = 0;
|
||||||
|
public channelCount = 0;
|
||||||
|
public liquidityNodesData: LookupNode[] = [];
|
||||||
|
public liquidityNodes: any;
|
||||||
|
public flgSticky = false;
|
||||||
|
public pageSize = PAGE_SIZE;
|
||||||
|
public pageSizeOptions = PAGE_SIZE_OPTIONS;
|
||||||
|
public screenSize = '';
|
||||||
|
public screenSizeEnum = ScreenSizeEnum;
|
||||||
|
public errorMessage = '';
|
||||||
|
public apiCallStatus: ApiCallStatusPayload = { status: APICallStatusEnum.INITIATED };
|
||||||
|
public apiCallStatusEnum = APICallStatusEnum;
|
||||||
|
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
|
||||||
|
|
||||||
|
constructor(private logger: LoggerService, private dataService: DataService, private commonService: CommonService) {
|
||||||
|
this.screenSize = this.commonService.getScreenSize();
|
||||||
|
if (this.screenSize === ScreenSizeEnum.XS) {
|
||||||
|
this.flgSticky = false;
|
||||||
|
this.displayedColumns = ['alias', 'capacity', 'actions'];
|
||||||
|
} else if (this.screenSize === ScreenSizeEnum.SM) {
|
||||||
|
this.flgSticky = false;
|
||||||
|
this.displayedColumns = ['alias', 'capacity', 'numChannels', 'leaseFeeBasis', 'routingFee', 'channelOpenFee', 'actions'];
|
||||||
|
} else if (this.screenSize === ScreenSizeEnum.MD) {
|
||||||
|
this.flgSticky = false;
|
||||||
|
this.displayedColumns = ['alias', 'capacity', 'numChannels', 'leaseFeeBasis', 'routingFee', 'channelOpenFee', 'actions'];
|
||||||
|
} else {
|
||||||
|
this.flgSticky = true;
|
||||||
|
this.displayedColumns = ['alias', 'capacity', 'numChannels', 'leaseFeeBasis', 'routingFee', 'channelOpenFee', 'actions'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.dataService.listNetworkNodes('?liquidity_ads=yes').pipe(takeUntil(this.unSubs[0])).subscribe({
|
||||||
|
next: (res: any) => {
|
||||||
|
this.logger.info('Received Liquidity Ads Enabled Nodes: ' + JSON.stringify(res));
|
||||||
|
this.apiCallStatus.status = APICallStatusEnum.COMPLETED;
|
||||||
|
this.liquidityNodesData = res;
|
||||||
|
this.loadLiqNodesTable(this.liquidityNodesData);
|
||||||
|
}, error: (err) => {
|
||||||
|
this.logger.error('Liquidity Ads Nodes Error: ' + JSON.stringify(err));
|
||||||
|
this.apiCallStatus.status = APICallStatusEnum.ERROR;
|
||||||
|
this.errorMessage = JSON.stringify(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRecalculate() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilter() {
|
||||||
|
this.logger.info(this.nodeCapacity);
|
||||||
|
this.logger.info(this.channelCount);
|
||||||
|
// this.liquidityNodes.filter = this.nodeCapacity + ' ' + this.channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLiqNodesTable(liqNodes: LookupNode[]) {
|
||||||
|
this.liquidityNodes = new MatTableDataSource<LookupNode>([...liqNodes]);
|
||||||
|
this.liquidityNodes.sortingDataAccessor = (data: any, sortHeaderId: string) => ((data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : data[sortHeaderId] ? +data[sortHeaderId] : null);
|
||||||
|
this.liquidityNodes.sort = this.sort;
|
||||||
|
this.liquidityNodes.filterPredicate = (node: LookupNode, fltr: string) => JSON.stringify(node).toLowerCase().includes(fltr);
|
||||||
|
this.liquidityNodes.paginator = this.paginator;
|
||||||
|
this.onFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpenChannel(lqNode: LookupNode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(lqNode: LookupNode) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onDownloadCSV() {
|
||||||
|
if (this.liquidityNodes.data && this.liquidityNodes.data.length > 0) {
|
||||||
|
this.commonService.downloadFile(this.liquidityNodes.data, 'LiquidityNodes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReset() {
|
||||||
|
this.channelAmount = 0;
|
||||||
|
this.channelOpeningFeeRate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
onFilterReset() {
|
||||||
|
this.nodeCapacity = 0;
|
||||||
|
this.channelCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.unSubs.forEach((completeSub) => {
|
||||||
|
completeSub.next(null);
|
||||||
|
completeSub.complete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue