commit
5cb6e10bd2
@ -1,23 +0,0 @@
|
||||
name: Pull Request Stats
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, 'Release-*' ]
|
||||
tags: [ 'v*' ]
|
||||
release:
|
||||
types: [released]
|
||||
# Triggers the workflow only when merging pull request to the branches.
|
||||
pull_request:
|
||||
types: [opened, closed]
|
||||
branches: [ master, 'Release-*', '*' ]
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Run pull request stats
|
||||
uses: flowwer-dev/pull-request-stats@master
|
||||
with:
|
||||
period: 365
|
@ -1,11 +1,12 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel } from '../../controllers/eclair/channels.js';
|
||||
import { getChannels, getChannelStats, openChannel, updateChannelRelayFee, closeChannel, circularRebalance } from '../../controllers/eclair/channels.js';
|
||||
const router = Router();
|
||||
router.get('/', isAuthenticated, getChannels);
|
||||
router.get('/stats', isAuthenticated, getChannelStats);
|
||||
router.post('/', isAuthenticated, openChannel);
|
||||
router.post('/updateRelayFee', isAuthenticated, updateChannelRelayFee);
|
||||
router.post('/circularRebalance', circularRebalance);
|
||||
router.delete('/', isAuthenticated, closeChannel);
|
||||
export default router;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getNodes } from '../../controllers/eclair/network.js';
|
||||
import { getNodes, findRouteBetweenNodes } from '../../controllers/eclair/network.js';
|
||||
const router = Router();
|
||||
router.get('/nodes/:id', isAuthenticated, getNodes);
|
||||
router.get('/routebetweennodes', isAuthenticated, findRouteBetweenNodes);
|
||||
export default router;
|
||||
|
@ -1,10 +1,11 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment } from '../../controllers/eclair/payments.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment, sendPaymentToRoute } from '../../controllers/eclair/payments.js';
|
||||
const router = Router();
|
||||
router.get('/route/', isAuthenticated, queryPaymentRoute);
|
||||
router.get('/decode/:invoice', isAuthenticated, decodePayment);
|
||||
router.post('/getsentinfos', isAuthenticated, getSentPaymentsInformation);
|
||||
router.post('/sendtoroute', isAuthenticated, sendPaymentToRoute);
|
||||
router.post('/', isAuthenticated, postPayment);
|
||||
export default router;
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
||||
(()=>{"use strict";var e,v={},m={};function r(e){var o=m[e];if(void 0!==o)return o.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(o,t,i,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,f]=e[n],s=!0,l=0;l<t.length;l++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(s=!1,f<a&&(a=f));if(s){e.splice(n--,1);var d=i();void 0!==d&&(o=d)}}return o}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,i,f]},r.d=(e,o)=>{for(var t in o)r.o(o,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((o,t)=>(r.f[t](e,o),o),[])),r.u=e=>e+"."+{167:"bba0ab48235f9054",267:"5508f97536cb5708",315:"d20113f8d2f54786",636:"8e6af702364d1f11"}[e]+".js",r.miniCssF=e=>{},r.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),(()=>{var e={},o="RTLApp:";r.l=(t,i,f,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==f)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==o+f){a=u;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",o+f),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:o=>o},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,f)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)f.push(n[2]);else if(666!=i){var a=new Promise((u,c)=>n=e[i]=[u,c]);f.push(n[2]=a);var s=r.p+r.u(i),l=new Error;r.l(s,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",l.name="ChunkLoadError",l.type=c,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var o=(i,f)=>{var l,d,[n,a,s]=f,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(s)var c=s(r)}for(i&&i(f);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[d]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})()})();
|
@ -1 +0,0 @@
|
||||
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var u=i();void 0!==u&&(f=u)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{258:"74bd2ac767e7a584",267:"8f996ec2b4b156e0",564:"34d502899be1574e",636:"0bd12e29e4623b7e"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var l=d[u];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==f+o){a=l;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((l,c)=>n=e[i]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(i),d=new Error;r.l(s,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,n[1](d)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var d,u,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(i&&i(o);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { getNodes } from '../../controllers/eclair/network.js';
|
||||
import { getNodes, findRouteBetweenNodes } from '../../controllers/eclair/network.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/nodes/:id', isAuthenticated, getNodes);
|
||||
|
||||
router.get('/routebetweennodes', isAuthenticated, findRouteBetweenNodes);
|
||||
export default router;
|
||||
|
@ -1,13 +1,14 @@
|
||||
import exprs from 'express';
|
||||
const { Router } = exprs;
|
||||
import { isAuthenticated } from '../../utils/authCheck.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment } from '../../controllers/eclair/payments.js';
|
||||
import { queryPaymentRoute, decodePayment, getSentPaymentsInformation, postPayment, sendPaymentToRoute } from '../../controllers/eclair/payments.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/route/', isAuthenticated, queryPaymentRoute);
|
||||
router.get('/decode/:invoice', isAuthenticated, decodePayment);
|
||||
router.post('/getsentinfos', isAuthenticated, getSentPaymentsInformation);
|
||||
router.post('/sendtoroute', isAuthenticated, sendPaymentToRoute);
|
||||
router.post('/', isAuthenticated, postPayment);
|
||||
|
||||
export default router;
|
||||
|
@ -1,3 +1,3 @@
|
||||
.fee-rate-list .mat-list-item {
|
||||
height: 44px;
|
||||
}
|
||||
height: 44px;
|
||||
}
|
||||
|
@ -0,0 +1,146 @@
|
||||
<div fxLayout="column" class="padding-gap">
|
||||
<div fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="page-sub-title-container">
|
||||
<div fxFlex="70"></div>
|
||||
<div fxFlex.gt-xs="30" fxLayoutAlign.gt-xs="space-between center" fxLayout="row" fxLayoutAlign="space-between stretch">
|
||||
<mat-form-field fxLayout="column" fxFlex="49">
|
||||
<mat-label>Filter By</mat-label>
|
||||
<mat-select tabindex="1" name="filterBy" [(ngModel)]="selFilterBy" (selectionChange)="selFilter=''; applyFilter()">
|
||||
<perfect-scrollbar><mat-option *ngFor="let column of ['all'].concat(displayedColumns.slice(0, -1))" [value]="column">{{getLabel(column)}}</mat-option></perfect-scrollbar>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field fxLayout="column" fxFlex="49">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput name="filter" [(ngModel)]="selFilter" (input)="applyFilter()" (keyup)="applyFilter()">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
<div fxLayout="column" fxFlex="100" class="table-container" [perfectScrollbar]>
|
||||
<mat-progress-bar *ngIf="apiCallStatus.status === apiCallStatusEnum.INITIATED" mode="indeterminate"></mat-progress-bar>
|
||||
<table #table mat-table fxFlex="100" matSort [matSortActive]="tableSetting.sortBy" [matSortDirection]="tableSetting.sortOrder" [dataSource]="channels" [ngClass]="{'error-border': errorMessage !== ''}">
|
||||
<!-- Channel Group Row Start -->
|
||||
<ng-container matColumnDef="amount_msat">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header>Amount (Sats)</th>
|
||||
<td *matCellDef="let channel" mat-cell>
|
||||
<span fxLayoutAlign="start center" class="htlc-row-span">
|
||||
Active HTLCs: {{channel?.htlcs?.length}}
|
||||
</span>
|
||||
<ng-container *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs; index as i" fxLayoutAlign="end center" class="htlc-row-span">
|
||||
{{htlc?.amount_msat / 1000 | number:'1.0-2'}}
|
||||
</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="direction">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header>Alias/Direction</th>
|
||||
<td *matCellDef="let channel" mat-cell>
|
||||
<span fxLayoutAlign="start center" class="htlc-row-span">{{channel?.alias}}</span>
|
||||
<ng-container *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="start center" class="htlc-row-span">
|
||||
{{htlc?.direction | titlecase}}
|
||||
</span>
|
||||
</ng-container>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="id">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">HTLC ID</span>
|
||||
</th>
|
||||
<td *matCellDef="let channel" mat-cell>
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">{{channel?.id}}</span>
|
||||
<span *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="end center" class="htlc-row-span">
|
||||
{{htlc?.id | number}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="expiry">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">Expiry</span>
|
||||
</th>
|
||||
<td *matCellDef="let channel" mat-cell>
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">{{' '}}</span>
|
||||
<span *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="end center" class="htlc-row-span">
|
||||
{{htlc?.expiry | number:'1.0-0'}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="state">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" class="pl-3 htlc-row-span">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">State</span>
|
||||
</th>
|
||||
<td *matCellDef="let channel" mat-cell class="pl-3">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">{{' '}}</span>
|
||||
<span *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="end center" class="htlc-row-span">
|
||||
{{htlc?.state | camelcaseWithReplace:'_'}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="local_trimmed">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" class="pl-3 htlc-row-span">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">Local Trimmed</span>
|
||||
</th>
|
||||
<td *matCellDef="let channel" mat-cell class="pl-3">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">{{' '}}</span>
|
||||
<span *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="end center" class="htlc-row-span">
|
||||
{{htlc?.local_trimmed ? 'Yes' : 'No'}}
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="payment_hash">
|
||||
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before" class="pl-3 htlc-row-span">
|
||||
<span fxLayoutAlign="end center" class="htlc-row-span">Payment Hash</span>
|
||||
</th>
|
||||
<td *matCellDef="let channel" mat-cell class="pl-3">
|
||||
<span fxLayout="row" class="ellipsis-parent htlc-row-span" [ngStyle]="{'width': (screenSize === screenSizeEnum.XS) ? '6rem' : colWidth}">
|
||||
<span fxLayoutAlign="end center" class="ellipsis-child">{{' '}}</span>
|
||||
</span>
|
||||
<span *ngIf="channel.is_expanded">
|
||||
<span *ngFor="let htlc of channel?.htlcs" fxLayoutAlign="start center" class="ellipsis-parent htlc-row-span" [ngStyle]="{'width': (screenSize === screenSizeEnum.XS) ? '6rem' : colWidth}">
|
||||
<span class="ellipsis-child">{{htlc?.payment_hash}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="actions">
|
||||
<th *matHeaderCellDef mat-header-cell class="px-2">
|
||||
<div class="bordered-box table-actions-select" fxLayoutAlign="end center">
|
||||
<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 *matCellDef="let channel" mat-cell class="px-2" fxLayout="column" fxLayoutAlign="center end">
|
||||
<span fxLayoutAlign="end center" class="htlc-group-head">
|
||||
<button mat-flat-button class="btn-htlc-expand" color="primary" type="button" tabindex="5" (click)="channel.is_expanded = !channel.is_expanded">{{channel.is_expanded ? 'Hide' : 'Show'}}</button>
|
||||
</span>
|
||||
<div *ngIf="channel.is_expanded">
|
||||
<div *ngFor="let htlc of channel?.htlcs; index as i" class="htlc-group-details" fxLayoutAlign="end center">
|
||||
<button mat-stroked-button class="btn-htlc-info" color="primary" type="button" tabindex="6" (click)="onHTLCClick(htlc, channel)">View {{i + 1}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
<ng-container matColumnDef="no_channel">
|
||||
<td *matFooterCellDef mat-footer-cell colspan="4">
|
||||
<p *ngIf="(!channels?.data || channels?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.COMPLETED">No active htlc available.</p>
|
||||
<p *ngIf="(!channels?.data || channels?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.INITIATED">Getting active htlcs...</p>
|
||||
<p *ngIf="(!channels?.data || channels?.data?.length<1) && apiCallStatus.status === apiCallStatusEnum.ERROR">{{errorMessage}}</p>
|
||||
</td>
|
||||
</ng-container>
|
||||
<!-- channel Group Row End -->
|
||||
<tr *matFooterRowDef="['no_channel']" mat-footer-row [ngClass]="{'display-none': channels?.data && channels?.data?.length>0}"></tr>
|
||||
<tr *matHeaderRowDef="displayedColumns" mat-header-row></tr>
|
||||
<tr *matRowDef="let row; columns: displayedColumns;" mat-row></tr>
|
||||
</table>
|
||||
</div>
|
||||
<mat-paginator class="mb-1" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true"></mat-paginator>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
@import "../../../../../shared/theme/styles/constants.scss";
|
||||
|
||||
.mat-column-amount_msat {
|
||||
.htlc-row-span:not(:first-of-type) {
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.htlc-row-span {
|
||||
min-height: 3rem;
|
||||
&.ellipsis-parent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-column-actions {
|
||||
& .htlc-group-head, & .htlc-group-details {
|
||||
min-height: 3rem;
|
||||
}
|
||||
|
||||
& .btn-htlc-expand {
|
||||
min-width: $table-actions-min-width;
|
||||
width: $table-actions-min-width;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& .btn-htlc-info {
|
||||
min-width: $table-actions-min-width - 1rem;
|
||||
min-width: $table-actions-min-width - 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
@ -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 '../../../../../shared/services/common.service';
|
||||
import { LoggerService } from '../../../../../shared/services/logger.service';
|
||||
|
||||
import { CLNChannelActiveHTLCsTableComponent } from './channel-active-htlcs-table.component';
|
||||
import { mockDataService, mockLoggerService } from '../../../../../shared/test-helpers/mock-services';
|
||||
import { SharedModule } from '../../../../../shared/shared.module';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DataService } from '../../../../../shared/services/data.service';
|
||||
|
||||
describe('CLNChannelActiveHTLCsTableComponent', () => {
|
||||
let component: CLNChannelActiveHTLCsTableComponent;
|
||||
let fixture: ComponentFixture<CLNChannelActiveHTLCsTableComponent>;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [CLNChannelActiveHTLCsTableComponent],
|
||||
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(CLNChannelActiveHTLCsTableComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
TestBed.resetTestingModule();
|
||||
});
|
||||
});
|
@ -0,0 +1,242 @@
|
||||
import { Component, OnInit, OnDestroy, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { MatPaginator, MatPaginatorIntl } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
import { Channel, ChannelHTLC } from '../../../../../shared/models/clnModels';
|
||||
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, APICallStatusEnum, SortOrderEnum, CLN_DEFAULT_PAGE_SETTINGS, CLN_PAGE_DEFS } from '../../../../../shared/services/consts-enums-functions';
|
||||
import { ApiCallStatusPayload } from '../../../../../shared/models/apiCallsPayload';
|
||||
import { LoggerService } from '../../../../../shared/services/logger.service';
|
||||
import { CommonService } from '../../../../../shared/services/common.service';
|
||||
|
||||
import { openAlert } from '../../../../../store/rtl.actions';
|
||||
import { RTLState } from '../../../../../store/rtl.state';
|
||||
import { clnPageSettings, channels } from '../../../../store/cln.selector';
|
||||
import { ColumnDefinition, PageSettings, TableSetting } from '../../../../../shared/models/pageSettings';
|
||||
import { CamelCaseWithReplacePipe } from '../../../../../shared/pipes/app.pipe';
|
||||
import { MAT_SELECT_CONFIG } from '@angular/material/select';
|
||||
|
||||
@Component({
|
||||
selector: 'rtl-cln-channel-active-htlcs-table',
|
||||
templateUrl: './channel-active-htlcs-table.component.html',
|
||||
styleUrls: ['./channel-active-htlcs-table.component.scss'],
|
||||
providers: [
|
||||
{ provide: MAT_SELECT_CONFIG, useValue: { overlayPanelClass: 'rtl-select-overlay' } },
|
||||
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('HTLCs') }
|
||||
]
|
||||
})
|
||||
export class CLNChannelActiveHTLCsTableComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
|
||||
@ViewChild(MatSort, { static: false }) sort: MatSort | undefined;
|
||||
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator | undefined;
|
||||
public nodePageDefs = CLN_PAGE_DEFS;
|
||||
public selFilterBy = 'all';
|
||||
public colWidth = '20rem';
|
||||
public PAGE_ID = 'peers_channels';
|
||||
public tableSetting: TableSetting = { tableId: 'active_HTLCs', recordsPerPage: PAGE_SIZE, sortBy: 'expiry', sortOrder: SortOrderEnum.DESCENDING };
|
||||
public channels: any = new MatTableDataSource([]);
|
||||
public channelsJSONArr: Channel[] = [];
|
||||
public displayedColumns: any[] = [];
|
||||
public htlcColumns = [];
|
||||
public pageSize = PAGE_SIZE;
|
||||
public pageSizeOptions = PAGE_SIZE_OPTIONS;
|
||||
public screenSize = '';
|
||||
public screenSizeEnum = ScreenSizeEnum;
|
||||
public errorMessage = '';
|
||||
public selFilter = '';
|
||||
public apiCallStatus: ApiCallStatusPayload | null = null;
|
||||
public apiCallStatusEnum = APICallStatusEnum;
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
|
||||
|
||||
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<RTLState>, private camelCaseWithReplace: CamelCaseWithReplacePipe) {
|
||||
this.screenSize = this.commonService.getScreenSize();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.store.select(clnPageSettings).pipe(takeUntil(this.unSubs[0])).
|
||||
subscribe((settings: { pageSettings: PageSettings[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.errorMessage = '';
|
||||
this.apiCallStatus = settings.apiCallStatus;
|
||||
if (this.apiCallStatus.status === APICallStatusEnum.ERROR) {
|
||||
this.errorMessage = this.apiCallStatus.message || '';
|
||||
}
|
||||
this.tableSetting = settings.pageSettings.find((page) => page.pageId === this.PAGE_ID)?.tables.find((table) => table.tableId === this.tableSetting.tableId) || CLN_DEFAULT_PAGE_SETTINGS.find((page) => page.pageId === this.PAGE_ID)?.tables.find((table) => table.tableId === this.tableSetting.tableId)!;
|
||||
if (this.screenSize === ScreenSizeEnum.XS || this.screenSize === ScreenSizeEnum.SM) {
|
||||
this.displayedColumns = JSON.parse(JSON.stringify(this.tableSetting.columnSelectionSM));
|
||||
} else {
|
||||
this.displayedColumns = JSON.parse(JSON.stringify(this.tableSetting.columnSelection));
|
||||
}
|
||||
this.displayedColumns.push('actions');
|
||||
this.pageSize = this.tableSetting.recordsPerPage ? +this.tableSetting.recordsPerPage : PAGE_SIZE;
|
||||
this.colWidth = this.displayedColumns.length ? ((this.commonService.getContainerSize().width / this.displayedColumns.length) / 14) + 'rem' : '20rem';
|
||||
this.logger.info(this.displayedColumns);
|
||||
});
|
||||
this.store.select(channels).pipe(takeUntil(this.unSubs[1])).
|
||||
subscribe((channelsSelector: { activeChannels: Channel[], pendingChannels: Channel[], inactiveChannels: Channel[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.errorMessage = '';
|
||||
this.apiCallStatus = channelsSelector.apiCallStatus;
|
||||
if (this.apiCallStatus.status === APICallStatusEnum.ERROR) {
|
||||
this.errorMessage = !this.apiCallStatus.message ? '' : (typeof (this.apiCallStatus.message) === 'object') ? JSON.stringify(this.apiCallStatus.message) : this.apiCallStatus.message;
|
||||
}
|
||||
const allChannels = [...channelsSelector.activeChannels, ...channelsSelector.pendingChannels, ...channelsSelector.inactiveChannels];
|
||||
this.channelsJSONArr = allChannels?.filter((channel) => channel.htlcs && channel.htlcs.length > 0) || [];
|
||||
if (this.channelsJSONArr.length > 0 && this.sort && this.paginator && this.displayedColumns.length > 0) {
|
||||
this.loadHTLCsTable(this.channelsJSONArr);
|
||||
}
|
||||
this.logger.info(channelsSelector);
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (this.channelsJSONArr.length > 0) {
|
||||
this.loadHTLCsTable(this.channelsJSONArr);
|
||||
}
|
||||
}
|
||||
|
||||
onHTLCClick(selHtlc: ChannelHTLC, selChannel: Channel) {
|
||||
const reorderedHTLC = [
|
||||
[{ key: 'alias', value: selChannel.alias, title: 'Alias', width: 100, type: DataTypeEnum.STRING }],
|
||||
[{ key: 'amount_msat', value: ((selHtlc.amount_msat || 0) / 1000), title: 'Amount (Sats)', width: 50, type: DataTypeEnum.NUMBER },
|
||||
{ key: 'direction', value: this.commonService.titleCase(selHtlc.direction || ''), title: 'Direction', width: 50, type: DataTypeEnum.STRING }],
|
||||
[{ key: 'expiry', value: selHtlc.expiry, title: 'Expiry', width: 50, type: DataTypeEnum.NUMBER },
|
||||
{ key: 'state', value: this.camelCaseWithReplace.transform((selHtlc.state || '') || '', '_'), title: 'State', width: 50, type: DataTypeEnum.STRING }],
|
||||
[{ key: 'id', value: selHtlc.id, title: 'HTLC ID', width: 50, type: DataTypeEnum.STRING },
|
||||
{ key: 'local_trimmed', value: selHtlc.local_trimmed, title: 'Local Trimmed', width: 50, type: DataTypeEnum.BOOLEAN }],
|
||||
[{ key: 'payment_hash', value: selHtlc.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING }]
|
||||
];
|
||||
this.store.dispatch(openAlert({
|
||||
payload: {
|
||||
data: {
|
||||
type: AlertTypeEnum.INFORMATION,
|
||||
alertTitle: 'HTLC Information',
|
||||
message: reorderedHTLC
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
applyFilter() {
|
||||
this.channels.filter = this.selFilter.trim().toLowerCase();
|
||||
}
|
||||
|
||||
getLabel(column: string) {
|
||||
const returnColumn: ColumnDefinition = this.nodePageDefs[this.PAGE_ID][this.tableSetting.tableId].allowedColumns.find((col) => col.column === column);
|
||||
return returnColumn ? returnColumn.label ? returnColumn.label : this.camelCaseWithReplace.transform((returnColumn.column || ''), '_') : this.commonService.titleCase(column);
|
||||
}
|
||||
|
||||
setFilterPredicate() {
|
||||
this.channels.filterPredicate = (rowData: Channel, fltr: string) => {
|
||||
let rowToFilter = '';
|
||||
switch (this.selFilterBy) {
|
||||
case 'all':
|
||||
rowToFilter = (rowData.alias ? rowData.alias.toLowerCase() : '') +
|
||||
rowData.htlcs?.map((htlc) => JSON.stringify(htlc).toLowerCase() + (htlc.local_trimmed ? ' yes ' : ' no '));
|
||||
break;
|
||||
|
||||
case 'direction':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => htlc.direction + ' ').toString() || '';
|
||||
break;
|
||||
|
||||
case 'id':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => htlc.id + ' ').toString() || '';
|
||||
break;
|
||||
|
||||
case 'expiry':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => htlc.expiry + ' ').toString() || '';
|
||||
break;
|
||||
|
||||
case 'state':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => this.camelCaseWithReplace.transform((htlc.state || '') || '', '_').toLowerCase() + ' ').toString() || '';
|
||||
break;
|
||||
|
||||
case 'payment_hash':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => htlc.payment_hash + ' ').toString() || '';
|
||||
break;
|
||||
|
||||
case 'local_trimmed':
|
||||
rowToFilter = rowData.htlcs?.map((htlc) => (htlc.local_trimmed ? ' yes ' : ' no ')).toString() || '';
|
||||
break;
|
||||
|
||||
case 'amount_msat':
|
||||
rowToFilter = (rowData.htlcs?.map((htlc) => (htlc.amount_msat || 0) / 1000))?.toString() || '';
|
||||
break;
|
||||
|
||||
default:
|
||||
rowToFilter = typeof rowData[this.selFilterBy] === 'undefined' ? '' : typeof rowData[this.selFilterBy] === 'string' ? rowData[this.selFilterBy].toLowerCase() : typeof rowData[this.selFilterBy] === 'boolean' ? (rowData[this.selFilterBy] ? 'yes' : 'no') : rowData[this.selFilterBy].toString();
|
||||
break;
|
||||
}
|
||||
return rowToFilter.includes(fltr);
|
||||
};
|
||||
}
|
||||
|
||||
loadHTLCsTable(channels: Channel[]) {
|
||||
this.channels = (channels) ? new MatTableDataSource<Channel>([...channels]) : new MatTableDataSource([]);
|
||||
this.channels.sort = this.sort;
|
||||
this.channels.sortingDataAccessor = (data: any, sortHeaderId: string) => {
|
||||
switch (sortHeaderId) {
|
||||
case 'amount_msat':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'number', this.sort?.direction);
|
||||
return data.htlcs && data.htlcs.length ? data.htlcs.length : null;
|
||||
|
||||
case 'id':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'string', this.sort?.direction);
|
||||
return data;
|
||||
|
||||
case 'direction':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'string', this.sort?.direction);
|
||||
return data.alias ? data.alias : data.id ? data.id : null;
|
||||
|
||||
case 'expiry':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'number', this.sort?.direction);
|
||||
return data;
|
||||
|
||||
case 'payment_hash':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'string', this.sort?.direction);
|
||||
return data;
|
||||
|
||||
case 'state':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'string', this.sort?.direction);
|
||||
return data;
|
||||
|
||||
case 'local_trimmed':
|
||||
this.commonService.sortByKey(data.htlcs, sortHeaderId, 'boolean', this.sort?.direction);
|
||||
return data;
|
||||
|
||||
default:
|
||||
return (data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : data[sortHeaderId] ? +data[sortHeaderId] : null;
|
||||
}
|
||||
};
|
||||
this.channels.paginator = this.paginator;
|
||||
this.setFilterPredicate();
|
||||
this.applyFilter();
|
||||
}
|
||||
|
||||
onDownloadCSV() {
|
||||
if (this.channels.data && this.channels.data.length > 0) {
|
||||
this.commonService.downloadFile(this.flattenHTLCs(), 'ActiveHTLCs');
|
||||
}
|
||||
}
|
||||
|
||||
flattenHTLCs() {
|
||||
const channelsDataCopy = JSON.parse(JSON.stringify(this.channels.data));
|
||||
const flattenedHTLCs = channelsDataCopy?.reduce((acc, curr) => {
|
||||
if (curr.htlcs) {
|
||||
return acc.concat(curr.htlcs);
|
||||
} else {
|
||||
return acc.concat(curr);
|
||||
}
|
||||
}, []);
|
||||
return flattenedHTLCs;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.unSubs.forEach((completeSub) => {
|
||||
completeSub.next(<any>null);
|
||||
completeSub.complete();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue