mirror of
https://github.com/Ride-The-Lightning/RTL.git
synced 2024-11-19 01:40:29 +01:00
Add pending htlcs to all channels tabs #1086
Add pending htlcs to all channels tabs #1086
This commit is contained in:
parent
f45bbc4ad2
commit
804ba91d7b
1
frontend/315.0621d0b32a4a191d.js
Normal file
1
frontend/315.0621d0b32a4a191d.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -13,6 +13,6 @@
|
||||
<style>html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:100%}@media only screen and (max-width: 56.25em){html{font-size:90%}}@media only screen and (max-width: 37.5em){html{font-size:80%}}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}@font-face{font-family:Roboto;src:url(Roboto-Thin.f7a95c9c5999532c.woff2) format("woff2"),url(Roboto-Thin.c13c157cb81e8ebb.woff) format("woff");font-weight:100;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.b0e084abf689f393.woff2) format("woff2"),url(Roboto-ThinItalic.1111028df6cea564.woff) format("woff");font-weight:100;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Light.0e01b6cd13b3857f.woff2) format("woff2"),url(Roboto-Light.603ca9a537b88428.woff) format("woff");font-weight:300;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.232ef4b20215f720.woff2) format("woff2"),url(Roboto-LightItalic.1b5e142f787151c8.woff) format("woff");font-weight:300;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Regular.475ba9e4e2d63456.woff2) format("woff2"),url(Roboto-Regular.bcefbfee882bc1cb.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.e3a9ebdaac06bbc4.woff2) format("woff2"),url(Roboto-RegularItalic.0668fae6af0cf8c2.woff) format("woff");font-weight:400;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Medium.457532032ceb0168.woff2) format("woff2"),url(Roboto-Medium.6e1ae5f0b324a0aa.woff) format("woff");font-weight:500;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.872f7060602d55d2.woff2) format("woff2"),url(Roboto-MediumItalic.e06fb533801cbb08.woff) format("woff");font-weight:500;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Bold.447291a88c067396.woff2) format("woff2"),url(Roboto-Bold.fc482e6133cf5e26.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.1b15168ef6fa4e16.woff2) format("woff2"),url(Roboto-BoldItalic.e26ba339b06f09f7.woff) format("woff");font-weight:700;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Black.2eaa390d458c877d.woff2) format("woff2"),url(Roboto-Black.b25f67ad8583da68.woff) format("woff");font-weight:900;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.7dc03ee444552bc5.woff2) format("woff2"),url(Roboto-BlackItalic.c8dc642467cb3099.woff) format("woff");font-weight:900;font-style:italic}</style><link rel="stylesheet" href="styles.d31e61a01689a167.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.d31e61a01689a167.css"></noscript></head>
|
||||
<body>
|
||||
<rtl-app></rtl-app>
|
||||
<script src="runtime.513b6ad72ee8ead8.js" type="module"></script><script src="polyfills.9720483e1820202a.js" type="module"></script><script src="main.8519111f4b579c59.js" type="module"></script>
|
||||
<script src="runtime.7f33f29c10c7c45e.js" type="module"></script><script src="polyfills.9720483e1820202a.js" type="module"></script><script src="main.583912e62d2e15ec.js" type="module"></script>
|
||||
|
||||
</body></html>
|
1
frontend/main.583912e62d2e15ec.js
Normal file
1
frontend/main.583912e62d2e15ec.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +0,0 @@
|
||||
(()=>{"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],c=!0,d=0;d<t.length;d++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(c=!1,f<a&&(a=f));if(c){e.splice(n--,1);var u=i();void 0!==u&&(o=u)}}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:"836d81485f16d9bc",267:"8f996ec2b4b156e0",564:"5cacf70cdd7a222e",636:"c6beed2b2207416a"}[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,c;if(void 0!==f)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")==o+f){a=l;break}}a||(c=!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 s=(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(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&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((l,s)=>n=e[i]=[l,s]);f.push(n[2]=a);var c=r.p+r.u(i),d=new Error;r.l(c,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",d.name="ChunkLoadError",d.type=s,d.request=p,n[1](d)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var o=(i,f)=>{var d,u,[n,a,c]=f,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(c)var s=c(r)}for(i&&i(f);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})()})();
|
1
frontend/runtime.7f33f29c10c7c45e.js
Normal file
1
frontend/runtime.7f33f29c10c7c45e.js
Normal file
@ -0,0 +1 @@
|
||||
(()=>{"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],c=!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):(c=!1,o<a&&(a=o));if(c){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+"."+{167:"836d81485f16d9bc",267:"8f996ec2b4b156e0",315:"0621d0b32a4a191d",636:"c6beed2b2207416a"}[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,c;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||(c=!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 s=(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(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&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,s)=>n=e[i]=[l,s]);o.push(n[2]=a);var c=r.p+r.u(i),d=new Error;r.l(c,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",d.name="ChunkLoadError",d.type=s,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,c]=o,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(c)var s=c(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(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();
|
@ -58,6 +58,7 @@ import { CLNOffersTableComponent } from './transactions/offers/offers-table/offe
|
||||
import { CLNOfferBookmarksTableComponent } from './transactions/offers/offer-bookmarks-table/offer-bookmarks-table.component';
|
||||
import { CLNLiquidityAdsListComponent } from './liquidity-ads/liquidity-ads-list/liquidity-ads-list.component';
|
||||
import { CLNOpenLiquidityChannelComponent } from './liquidity-ads/open-liquidity-channel-modal/open-liquidity-channel-modal.component';
|
||||
import { CLNChannelActiveHTLCsTableComponent } from './peers-channels/channels/channels-tables/channel-active-htlcs-table/channel-active-htlcs-table.component';
|
||||
|
||||
import { CLNUnlockedGuard } from '../shared/services/auth.guard';
|
||||
|
||||
@ -121,7 +122,8 @@ import { CLNUnlockedGuard } from '../shared/services/auth.guard';
|
||||
CLNOffersTableComponent,
|
||||
CLNOfferBookmarksTableComponent,
|
||||
CLNLiquidityAdsListComponent,
|
||||
CLNOpenLiquidityChannelComponent
|
||||
CLNOpenLiquidityChannelComponent,
|
||||
CLNChannelActiveHTLCsTableComponent
|
||||
],
|
||||
providers: [
|
||||
CLNUnlockedGuard
|
||||
|
@ -24,6 +24,7 @@ import { CLNVerifyComponent } from './sign-verify-message/verify/verify.componen
|
||||
import { CLNForwardingHistoryComponent } from './routing/forwarding-history/forwarding-history.component';
|
||||
import { CLNFailedTransactionsComponent } from './routing/failed-transactions/failed-transactions.component';
|
||||
import { CLNRoutingPeersComponent } from './routing/routing-peers/routing-peers.component';
|
||||
import { CLNChannelActiveHTLCsTableComponent } from './peers-channels/channels/channels-tables/channel-active-htlcs-table/channel-active-htlcs-table.component';
|
||||
|
||||
import { CLNReportsComponent } from './reports/reports.component';
|
||||
import { CLNRoutingReportComponent } from './reports/routing/routing-report.component';
|
||||
@ -59,7 +60,8 @@ export const ClnRoutes: Routes = [
|
||||
path: 'channels', component: CLNChannelsTablesComponent, canActivate: [CLNUnlockedGuard], children: [
|
||||
{ path: '', pathMatch: <PathMatch>'full', redirectTo: 'open' },
|
||||
{ path: 'open', component: CLNChannelOpenTableComponent, canActivate: [CLNUnlockedGuard] },
|
||||
{ path: 'pending', component: CLNChannelPendingTableComponent, canActivate: [CLNUnlockedGuard] }
|
||||
{ path: 'pending', component: CLNChannelPendingTableComponent, canActivate: [CLNUnlockedGuard] },
|
||||
{ path: 'activehtlcs', component: CLNChannelActiveHTLCsTableComponent, canActivate: [CLNUnlockedGuard] }
|
||||
]
|
||||
},
|
||||
{ path: 'peers', component: CLNPeersComponent, data: { sweepAll: false }, canActivate: [CLNUnlockedGuard] }
|
||||
|
@ -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,243 @@
|
||||
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 { CLNChannelInformationComponent } from '../../channel-information-modal/channel-information.component';
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -14,6 +14,11 @@
|
||||
<span matBadgeOverlap="false" class="tab-badge" matBadge="{{pendingChannels}}">Pending/Inactive</span>
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
<mat-tab>
|
||||
<ng-template mat-tab-label>
|
||||
<span matBadgeOverlap="false" class="tab-badge" matBadge="{{activeHTLCs}}">Active HTLCs</span>
|
||||
</ng-template>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap-x-large">
|
||||
<router-outlet></router-outlet>
|
||||
|
@ -24,12 +24,13 @@ export class CLNChannelsTablesComponent implements OnInit, OnDestroy {
|
||||
|
||||
public openChannels = 0;
|
||||
public pendingChannels = 0;
|
||||
public activeHTLCs = 0;
|
||||
public selNode: SelNodeChild | null = {};
|
||||
public information: GetInfo = {};
|
||||
public peers: Peer[] = [];
|
||||
public utxos: UTXO[] = [];
|
||||
public totalBalance = 0;
|
||||
public links = [{ link: 'open', name: 'Open' }, { link: 'pending', name: 'Pending/Inactive' }];
|
||||
public links = [{ link: 'open', name: 'Open' }, { link: 'pending', name: 'Pending/Inactive' }, { link: 'activehtlcs', name: 'Active HTLCs' }];
|
||||
public activeLink = 0;
|
||||
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
|
||||
|
||||
@ -51,18 +52,20 @@ export class CLNChannelsTablesComponent implements OnInit, OnDestroy {
|
||||
this.logger.info(infoSettingsBalSelector);
|
||||
});
|
||||
this.store.select(peers).pipe(takeUntil(this.unSubs[2])).
|
||||
subscribe((peersSeletor: { peers: Peer[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.peers = peersSeletor.peers;
|
||||
subscribe((peersSelector: { peers: Peer[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.peers = peersSelector.peers;
|
||||
});
|
||||
this.store.select(utxos).pipe(takeUntil(this.unSubs[3])).
|
||||
subscribe((utxosSeletor: { utxos: UTXO[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.utxos = this.commonService.sortAscByKey(utxosSeletor.utxos?.filter((utxo) => utxo.status === 'confirmed'), 'value');
|
||||
});
|
||||
this.store.select(channels).pipe(takeUntil(this.unSubs[4])).
|
||||
subscribe((channelsSeletor: { activeChannels: Channel[], pendingChannels: Channel[], inactiveChannels: Channel[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.openChannels = channelsSeletor.activeChannels.length || 0;
|
||||
this.pendingChannels = (channelsSeletor.pendingChannels.length + channelsSeletor.inactiveChannels.length) || 0;
|
||||
this.logger.info(channelsSeletor);
|
||||
subscribe((channelsSelector: { activeChannels: Channel[], pendingChannels: Channel[], inactiveChannels: Channel[], apiCallStatus: ApiCallStatusPayload }) => {
|
||||
this.openChannels = channelsSelector.activeChannels.length || 0;
|
||||
this.pendingChannels = (channelsSelector.pendingChannels.length + channelsSelector.inactiveChannels.length) || 0;
|
||||
const allChannels = [...channelsSelector.activeChannels, ...channelsSelector.pendingChannels, ...channelsSelector.inactiveChannels];
|
||||
this.activeHTLCs = allChannels?.reduce((totalHTLCs, peer) => totalHTLCs + (peer.htlcs && peer.htlcs.length > 0 ? peer.htlcs.length : 0), 0);
|
||||
this.logger.info(channelsSelector);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ export class ECLChannelRebalanceComponent implements OnInit, OnDestroy {
|
||||
public selChannel: Channel = {};
|
||||
public activeChannels: Channel[] = [];
|
||||
public filteredActiveChannels: Observable<Channel[]>;
|
||||
public rebalanceStatus: { flgReusingInvoice: boolean, invoice: string, paymentHash: string, paymentDetails: any, paymentStatus: any } =
|
||||
public rebalanceStatus: { flgReusingInvoice: boolean, invoice: string, paymentHash: string, paymentDetails: any, paymentStatus: any } =
|
||||
{ flgReusingInvoice: false, invoice: '', paymentHash: '', paymentDetails: null, paymentStatus: null };
|
||||
public inputFormLabel = 'Amount to rebalance';
|
||||
public flgEditable = true;
|
||||
@ -106,26 +106,26 @@ export class ECLChannelRebalanceComponent implements OnInit, OnDestroy {
|
||||
if (!this.inputFormGroup.controls.rebalanceAmount.value || this.inputFormGroup.controls.rebalanceAmount.value <= 0 ||
|
||||
(this.selChannel.toLocal && this.inputFormGroup.controls.rebalanceAmount.value > +this.selChannel.toLocal) ||
|
||||
!this.inputFormGroup.controls.selRebalancePeer.value.nodeId) {
|
||||
if (!this.inputFormGroup.controls.selRebalancePeer.value.nodeId) {
|
||||
this.inputFormGroup.controls.selRebalancePeer.setErrors({required: true});
|
||||
}
|
||||
if (!this.inputFormGroup.controls.selRebalancePeer.value.nodeId) {
|
||||
this.inputFormGroup.controls.selRebalancePeer.setErrors({ required: true });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this.stepper.next();
|
||||
this.flgEditable = false;
|
||||
this.rebalanceStatus = { flgReusingInvoice: false, invoice: '', paymentHash: '', paymentDetails: null, paymentStatus: null };
|
||||
this.dataService.circularRebalance((this.inputFormGroup.controls.rebalanceAmount.value * 1000), this.selChannel.shortChannelId, this.selChannel.nodeId, this.inputFormGroup.controls.selRebalancePeer.value.shortChannelId, this.inputFormGroup.controls.selRebalancePeer.value.nodeId, [this.information.nodeId || '']).
|
||||
pipe(takeUntil(this.unSubs[2])).subscribe({
|
||||
next: (rebalanceRes) => {
|
||||
this.logger.info(rebalanceRes);
|
||||
this.rebalanceStatus = rebalanceRes;
|
||||
this.flgEditable = true;
|
||||
}, error: (error) => {
|
||||
this.logger.error(error);
|
||||
this.rebalanceStatus = error;
|
||||
this.flgEditable = true;
|
||||
}
|
||||
});
|
||||
this.dataService.circularRebalance((this.inputFormGroup.controls.rebalanceAmount.value * 1000), this.selChannel.shortChannelId, this.selChannel.nodeId, this.inputFormGroup.controls.selRebalancePeer.value.shortChannelId, this.inputFormGroup.controls.selRebalancePeer.value.nodeId, [this.information.nodeId || '']).
|
||||
pipe(takeUntil(this.unSubs[2])).subscribe({
|
||||
next: (rebalanceRes) => {
|
||||
this.logger.info(rebalanceRes);
|
||||
this.rebalanceStatus = rebalanceRes;
|
||||
this.flgEditable = true;
|
||||
}, error: (error) => {
|
||||
this.logger.error(error);
|
||||
this.rebalanceStatus = error;
|
||||
this.flgEditable = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filterActiveChannels() {
|
||||
|
@ -297,6 +297,16 @@ export interface QueryRoutes {
|
||||
routes: Routes[];
|
||||
}
|
||||
|
||||
export interface ChannelHTLC {
|
||||
direction?: string;
|
||||
id?: string;
|
||||
amount_msat?: number;
|
||||
expiry?: number;
|
||||
payment_hash?: string;
|
||||
state?: string;
|
||||
local_trimmed?: boolean;
|
||||
}
|
||||
|
||||
export interface Channel {
|
||||
id?: string;
|
||||
alias?: string;
|
||||
@ -313,6 +323,7 @@ export interface Channel {
|
||||
our_channel_reserve_satoshis?: string;
|
||||
spendable_msatoshi?: string;
|
||||
direction?: number;
|
||||
htlcs?: ChannelHTLC[];
|
||||
balancedness?: number; // Between 0-1-0
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,7 @@ export class CLNPageDefinitions {
|
||||
open_channels: TableDefinition;
|
||||
pending_inactive_channels: TableDefinition;
|
||||
peers: TableDefinition;
|
||||
active_HTLCs: TableDefinition;
|
||||
};
|
||||
liquidity_ads: {
|
||||
liquidity_ads: TableDefinition;
|
||||
|
@ -737,7 +737,10 @@ export const CLN_DEFAULT_PAGE_SETTINGS: PageSettings[] = [
|
||||
columnSelection: ['alias', 'connected', 'state', 'msatoshi_total'] },
|
||||
{ tableId: 'peers', recordsPerPage: PAGE_SIZE, sortBy: 'alias', sortOrder: SortOrderEnum.ASCENDING,
|
||||
columnSelectionSM: ['alias', 'id'],
|
||||
columnSelection: ['alias', 'id', 'netaddr'] }
|
||||
columnSelection: ['alias', 'id', 'netaddr'] },
|
||||
{ tableId: 'active_HTLCs', recordsPerPage: PAGE_SIZE, sortBy: 'expiry', sortOrder: SortOrderEnum.DESCENDING,
|
||||
columnSelectionSM: ['amount_msat', 'direction', 'expiry'],
|
||||
columnSelection: ['amount_msat', 'direction', 'expiry', 'state'] }
|
||||
] },
|
||||
{ pageId: 'liquidity_ads', tables: [
|
||||
{ tableId: 'liquidity_ads', recordsPerPage: PAGE_SIZE, sortBy: 'channel_opening_fee', sortOrder: SortOrderEnum.ASCENDING,
|
||||
@ -821,6 +824,11 @@ export const CLN_PAGE_DEFS: CLNPageDefinitions = {
|
||||
peers: {
|
||||
maxColumns: 3,
|
||||
allowedColumns: [{ column:'alias' }, { column:'id' }, { column:'netaddr', label: 'Network Address' }]
|
||||
},
|
||||
active_HTLCs: {
|
||||
maxColumns: 7,
|
||||
allowedColumns: [{ column:'amount_msat', label: 'Amount (Sats)' }, { column:'direction' }, { column:'id', label: 'HTLC ID' }, { column:'state' },
|
||||
{ column:'expiry' }, { column:'payment_hash' }, { column:'local_trimmed' }]
|
||||
}
|
||||
},
|
||||
liquidity_ads: {
|
||||
|
Loading…
Reference in New Issue
Block a user