CLT Out Channel for Failed Transaction #936 (#961)

CLT Out Channel for Failed Transaction #936
pull/962/head
ShahanaFarooqui 2 years ago committed by GitHub
parent 36df9e3dbe
commit f6f4897a0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -32,10 +32,7 @@ To download from master (*not recommended*):
```
$ git clone https://github.com/Ride-The-Lightning/RTL.git
$ cd RTL
$ npm install
$ npm run buildfrontend
$ npm run buildbackend
$ npm prune --production
$ npm install --only=prod
```
#### Or: Update existing build
@ -44,10 +41,7 @@ $ cd RTL
$ git reset --hard HEAD
$ git clean -f -d
$ git pull
$ npm install
$ npm run buildfrontend
$ npm run buildbackend
$ npm prune --production
$ npm install --only=prod
```
### <a name="prep"></a>Prep for Execution
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.

@ -28,10 +28,7 @@ To download from master (*not recommended*) follow the below instructions:
```
$ git clone https://github.com/Ride-The-Lightning/RTL.git
$ cd RTL
$ npm install
$ npm run buildfrontend
$ npm run buildbackend
$ npm prune --production
$ npm install --only=prod
```
#### Or: Update existing build
```
@ -39,10 +36,7 @@ $ cd RTL
$ git reset --hard HEAD
$ git clean -f -d
$ git pull
$ npm install
$ npm run buildfrontend
$ npm run buildbackend
$ npm prune --production
$ npm install --only=prod
```
### <a name="prep"></a>Prep for Execution
RTL requires its own config file `RTL-Config.json`, to start the server and provide user authentication on the app.

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>@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}html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:62.5%}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}</style><link rel="stylesheet" href="styles.30265dd456248897.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.30265dd456248897.css"></noscript></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.2333c8d3ea701fae.js" type="module"></script><script src="polyfills.6d989da208bd6fd1.js" type="module"></script><script src="main.a62c41c5d0b46466.js" type="module"></script>
<script src="runtime.965c547a32bf1275.js" type="module"></script><script src="polyfills.6d989da208bd6fd1.js" type="module"></script><script src="main.8d64d847ad28a9f1.js" type="module"></script>
</body></html>

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={},g={};function r(e){var i=g[e];if(void 0!==i)return i.exports;var t=g[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=(i,t,d,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,d,f]=e[n],c=!0,o=0;o<t.length;o++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[o]))?t.splice(o--,1):(c=!1,f<a&&(a=f));if(c){e.splice(n--,1);var l=d();void 0!==l&&(i=l)}}return i}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,d,f]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>e+"."+{632:"c2c1a562dee51ad7",637:"d5db97dfc93a68d5",859:"b131a27d1c5a6337",893:"bc205686870d1329"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="RTLApp:";r.l=(t,d,f,n)=>{if(e[t])e[t].push(d);else{var a,c;if(void 0!==f)for(var o=document.getElementsByTagName("script"),l=0;l<o.length;l++){var u=o[l];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==i+f){a=u;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",i+f),a.src=r.tu(t)),e[t]=[d];var s=(m,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(_=>_(b)),m)return m(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=>{"undefined"!=typeof Symbol&&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.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(d,f)=>{var n=r.o(e,d)?e[d]:void 0;if(0!==n)if(n)f.push(n[2]);else if(666!=d){var a=new Promise((u,s)=>n=e[d]=[u,s]);f.push(n[2]=a);var c=r.p+r.u(d),o=new Error;r.l(c,u=>{if(r.o(e,d)&&(0!==(n=e[d])&&(e[d]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;o.message="Loading chunk "+d+" failed.\n("+s+": "+p+")",o.name="ChunkLoadError",o.type=s,o.request=p,n[1](o)}},"chunk-"+d,d)}else e[d]=0},r.O.j=d=>0===e[d];var i=(d,f)=>{var o,l,[n,a,c]=f,u=0;if(n.some(p=>0!==e[p])){for(o in a)r.o(a,o)&&(r.m[o]=a[o]);if(c)var s=c(r)}for(d&&d(f);u<n.length;u++)r.o(e,l=n[u])&&e[l]&&e[l][0](),e[n[u]]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();

@ -0,0 +1 @@
(()=>{"use strict";var e,v={},g={};function r(e){var i=g[e];if(void 0!==i)return i.exports;var t=g[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=(i,t,f,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,f,o]=e[n],s=!0,u=0;u<t.length;u++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[u]))?t.splice(u--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var d=f();void 0!==d&&(i=d)}}return i}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,f,o]},r.n=e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return r.d(i,{a:i}),i},r.d=(e,i)=>{for(var t in i)r.o(i,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:i[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((i,t)=>(r.f[t](e,i),i),[])),r.u=e=>e+"."+{634:"3422c68706eb3ecb",637:"4087a8d2dcfa51ef",859:"244a409258895d95",893:"050b2235bfbe164a"}[e]+".js",r.miniCssF=e=>{},r.o=(e,i)=>Object.prototype.hasOwnProperty.call(e,i),(()=>{var e={},i="RTLApp:";r.l=(t,f,o,n)=>{if(e[t])e[t].push(f);else{var a,s;if(void 0!==o)for(var u=document.getElementsByTagName("script"),d=0;d<u.length;d++){var l=u[d];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==i+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",i+o),a.src=r.tu(t)),e[t]=[f];var c=(m,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(_=>_(b)),m)return m(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=>{"undefined"!=typeof Symbol&&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.tu=i=>(void 0===e&&(e={createScriptURL:t=>t},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e.createScriptURL(i))})(),r.p="",(()=>{var e={666:0};r.f.j=(f,o)=>{var n=r.o(e,f)?e[f]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=f){var a=new Promise((l,c)=>n=e[f]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(f),u=new Error;r.l(s,l=>{if(r.o(e,f)&&(0!==(n=e[f])&&(e[f]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;u.message="Loading chunk "+f+" failed.\n("+c+": "+p+")",u.name="ChunkLoadError",u.type=c,u.request=p,n[1](u)}},"chunk-"+f,f)}else e[f]=0},r.O.j=f=>0===e[f];var i=(f,o)=>{var u,d,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(u in a)r.o(a,u)&&(r.m[u]=a[u]);if(s)var c=s(r)}for(f&&f(o);l<n.length;l++)r.o(e,d=n[l])&&e[d]&&e[d][0](),e[n[l]]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(i.bind(null,0)),t.push=i.bind(null,t.push.bind(t))})()})();

@ -20,6 +20,7 @@ import { CLLookupsComponent } from './graph/lookups/lookups.component';
import { CLRoutingComponent } from './routing/routing.component';
import { CLForwardingHistoryComponent } from './routing/forwarding-history/forwarding-history.component';
import { CLFailedTransactionsComponent } from './routing/failed-transactions/failed-transactions.component';
import { CLLocalFailedTransactionsComponent } from './routing/local-failed-transactions/local-failed-transactions.component';
import { CLRoutingPeersComponent } from './routing/routing-peers/routing-peers.component';
import { CLChannelLookupComponent } from './graph/lookups/channel-lookup/channel-lookup.component';
import { CLNodeLookupComponent } from './graph/lookups/node-lookup/node-lookup.component';
@ -76,6 +77,7 @@ import { CLUnlockedGuard } from '../shared/services/auth.guard';
CLRoutingComponent,
CLForwardingHistoryComponent,
CLFailedTransactionsComponent,
CLLocalFailedTransactionsComponent,
CLRoutingPeersComponent,
CLChannelLookupComponent,
CLNodeLookupComponent,

@ -33,6 +33,7 @@ import { NotFoundComponent } from '../shared/components/not-found/not-found.comp
import { CLGraphComponent } from './graph/graph.component';
import { CLOffersTableComponent } from './transactions/offers/offers-table/offers-table.component';
import { CLOfferBookmarksTableComponent } from './transactions/offers/offer-bookmarks-table/offer-bookmarks-table.component';
import { CLLocalFailedTransactionsComponent } from './routing/local-failed-transactions/local-failed-transactions.component';
export const ClRoutes: Routes = [
{
@ -82,6 +83,7 @@ export const ClRoutes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'forwardinghistory' },
{ path: 'forwardinghistory', component: CLForwardingHistoryComponent, canActivate: [CLUnlockedGuard] },
{ path: 'failedtransactions', component: CLFailedTransactionsComponent, canActivate: [CLUnlockedGuard] },
{ path: 'localfail', component: CLLocalFailedTransactionsComponent, canActivate: [CLUnlockedGuard] },
{ path: 'routingpeers', component: CLRoutingPeersComponent, canActivate: [CLUnlockedGuard] }
]
},

@ -9,14 +9,14 @@
<div *ngIf="errorMessage === ''" [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]="failedForwardingEvents" fxFlex="100" matSort class="overflow-auto">
<ng-container matColumnDef="status">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Status</th>
<td mat-cell *matCellDef="let fhEvent">{{(fhEvent?.status === 'local_failed' ? 'local failed' : fhEvent?.status ) | titlecase}}</td>
</ng-container>
<ng-container matColumnDef="received_time">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Received Time</th>
<td mat-cell *matCellDef="let fhEvent">{{(fhEvent?.received_time * 1000) | date:'dd/MMM/y HH:mm'}}</td>
</ng-container>
<ng-container matColumnDef="resolved_time">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Resolved Time</th>
<td mat-cell *matCellDef="let fhEvent">{{(fhEvent?.resolved_time * 1000) | date:'dd/MMM/y HH:mm'}}</td>
</ng-container>
<ng-container matColumnDef="in_channel">
<th mat-header-cell *matHeaderCellDef mat-sort-header>In Channel</th>
<td mat-cell *matCellDef="let fhEvent">{{fhEvent?.in_channel_alias}}</td>
@ -29,6 +29,14 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount In (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.in_msatoshi/1000 | number:fhEvent?.in_msatoshi < 1000 ? '1.0-4' : '1.0-0'}}</span></td>
</ng-container>
<ng-container matColumnDef="out_msatoshi">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount Out (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.out_msatoshi/1000 | number:fhEvent?.out_msatoshi < 1000 ? '1.0-4' : '1.0-0'}}</span></td>
</ng-container>
<ng-container matColumnDef="fee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee (mSats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.fee | 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">

@ -24,7 +24,7 @@ import { failedForwardingHistory } from '../../store/cl.selector';
templateUrl: './failed-transactions.component.html',
styleUrls: ['./failed-transactions.component.scss'],
providers: [
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('Events') }
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('Failed Events') }
]
})
export class CLFailedTransactionsComponent implements OnInit, AfterViewInit, OnDestroy {
@ -49,13 +49,13 @@ export class CLFailedTransactionsComponent implements OnInit, AfterViewInit, OnD
this.screenSize = this.commonService.getScreenSize();
if (this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['status', 'received_time', 'in_msatoshi', 'actions'];
this.displayedColumns = ['received_time', 'in_channel', 'in_msatoshi', 'actions'];
} else if (this.screenSize === ScreenSizeEnum.SM || this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['status', 'received_time', 'in_msatoshi', 'actions'];
this.displayedColumns = ['received_time', 'in_channel', 'out_channel', 'in_msatoshi', 'out_msatoshi', 'actions'];
} else {
this.flgSticky = true;
this.displayedColumns = ['status', 'received_time', 'in_channel', 'out_channel', 'in_msatoshi', 'actions'];
this.displayedColumns = ['received_time', 'resolved_time', 'in_channel', 'out_channel', 'in_msatoshi', 'out_msatoshi', 'fee', 'actions'];
}
}
@ -86,18 +86,22 @@ export class CLFailedTransactionsComponent implements OnInit, AfterViewInit, OnD
onFailedEventClick(selFEvent: ForwardingEvent) {
const reorderedFHEvent = [
[{ key: 'payment_hash', value: selFEvent.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING }],
[{ key: 'status', value: selFEvent.status === 'local_failed' ? 'Local Failed' : this.commonService.titleCase(selFEvent.status), title: 'Status', width: 50, type: DataTypeEnum.STRING },
{ key: 'received_time', value: selFEvent.received_time, title: 'Received Time', width: 50, type: DataTypeEnum.DATE_TIME }],
[{ key: 'in_channel', value: selFEvent.in_channel_alias, title: 'Inbound Channel', width: 50, type: DataTypeEnum.STRING },
{ key: 'out_channel', value: selFEvent.out_channel_alias, title: 'Outbound Channel', width: 50, type: DataTypeEnum.STRING },
{ key: 'in_msatoshi', value: selFEvent.in_msatoshi, title: 'In (mSats)', width: 50, type: DataTypeEnum.NUMBER }]
[{ key: 'received_time', value: selFEvent.received_time, title: 'Received Time', width: 50, type: DataTypeEnum.DATE_TIME },
{ key: 'resolved_time', value: selFEvent.resolved_time, title: 'Resolved Time', width: 50, type: DataTypeEnum.DATE_TIME }],
[{ key: 'in_channel_alias', value: selFEvent.in_channel_alias, title: 'Inbound Channel', width: 50, type: DataTypeEnum.STRING },
{ key: 'out_channel_alias', value: selFEvent.out_channel_alias, title: 'Outbound Channel', width: 50, type: DataTypeEnum.STRING }],
[{ key: 'in_msatoshi', value: selFEvent.in_msatoshi, title: 'Amount In (mSats)', width: 33, type: DataTypeEnum.NUMBER },
{ key: 'out_msatoshi', value: selFEvent.out_msatoshi, title: 'Amount Out (mSats)', width: 33, type: DataTypeEnum.NUMBER },
{ key: 'fee', value: selFEvent.fee, title: 'Fee (mSats)', width: 34, type: DataTypeEnum.NUMBER }]
];
if (selFEvent.payment_hash) {
reorderedFHEvent.unshift([{ key: 'payment_hash', value: selFEvent.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING }]);
}
this.store.dispatch(openAlert({
payload: {
data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Event Information',
alertTitle: 'Failed Event Information',
message: reorderedFHEvent
}
}
@ -110,11 +114,12 @@ export class CLFailedTransactionsComponent implements OnInit, AfterViewInit, OnD
this.failedForwardingEvents.sortingDataAccessor = (data: any, sortHeaderId: string) => ((data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : data[sortHeaderId] ? +data[sortHeaderId] : null);
this.failedForwardingEvents.paginator = this.paginator;
this.failedForwardingEvents.filterPredicate = (event: ForwardingEvent, fltr: string) => {
const newEvent = (event.status ? (event.status === 'local_failed') ? 'local failed' : event.status.toLowerCase() : '') +
(event.received_time ? this.datePipe.transform(new Date(event.received_time * 1000), 'dd/MMM/YYYY HH:mm').toLowerCase() : '') +
const newEvent = ((event.received_time ? this.datePipe.transform(new Date(event.received_time * 1000), 'dd/MMM/YYYY HH:mm').toLowerCase() : '') +
(event.resolved_time ? this.datePipe.transform(new Date(event.resolved_time * 1000), 'dd/MMM/YYYY HH:mm').toLowerCase() : '') +
(event.payment_hash ? event.payment_hash.toLowerCase() : '') +
(event.in_channel ? event.in_channel.toLowerCase() : '') + (event.out_channel ? event.out_channel.toLowerCase() : '') +
(event.in_msatoshi ? (event.in_msatoshi / 1000) : '') + (event.out_msatoshi ? (event.out_msatoshi / 1000) : '') + (event.fee ? event.fee : '');
(event.in_channel_alias ? event.in_channel_alias.toLowerCase() : '') + (event.out_channel_alias ? event.out_channel_alias.toLowerCase() : '') +
(event.in_msatoshi ? (event.in_msatoshi / 1000) : '') + (event.out_msatoshi ? (event.out_msatoshi / 1000) : '') + (event.fee ? event.fee : ''));
return newEvent.includes(fltr);
};
this.applyFilter();

@ -0,0 +1,54 @@
<div fxLayout="column" fxLayoutAlign="start stretch" class="padding-gap-x">
<div class="p-2 error-border my-2" *ngIf="errorMessage !== ''">{{errorMessage}}</div>
<div *ngIf="errorMessage === ''" fxLayout="column" fxLayout.gt-xs="row" fxLayoutAlign.gt-xs="start center" fxLayoutAlign="start stretch" class="page-sub-title-container">
<div fxFlex="70"></div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyFilter()" [(ngModel)]="selFilter" placeholder="Filter">
</mat-form-field>
</div>
<div *ngIf="errorMessage === ''" [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]="failedLocalForwardingEvents" fxFlex="100" matSort class="overflow-auto">
<ng-container matColumnDef="received_time">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Received Time</th>
<td mat-cell *matCellDef="let fhEvent">{{(fhEvent?.received_time * 1000) | date:'dd/MMM/y HH:mm'}}</td>
</ng-container>
<ng-container matColumnDef="in_channel">
<th mat-header-cell *matHeaderCellDef mat-sort-header>In Channel</th>
<td mat-cell *matCellDef="let fhEvent">{{fhEvent?.in_channel_alias}}</td>
</ng-container>
<ng-container matColumnDef="in_msatoshi">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount In (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.in_msatoshi/1000 | number:fhEvent?.in_msatoshi < 1000 ? '1.0-4' : '1.0-0'}}</span></td>
</ng-container>
<ng-container matColumnDef="failreason">
<th mat-header-cell *matHeaderCellDef mat-sort-header class="pl-3">Fail Reason</th>
<td mat-cell *matCellDef="let fhEvent" class="pl-3">{{CLFailReason[fhEvent?.failreason]}}</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 fhEvent" class="px-3" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onFailedLocalEventClick(fhEvent)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_event">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="(!failedLocalForwardingEvents?.data || failedLocalForwardingEvents?.data?.length<1) && apiCallStatus?.status === apiCallStatusEnum.COMPLETED">No failed transaction available.</p>
<p *ngIf="(!failedLocalForwardingEvents?.data || failedLocalForwardingEvents?.data?.length<1) && apiCallStatus?.status === apiCallStatusEnum.INITIATED">Getting failed transactions...</p>
<p *ngIf="(!failedLocalForwardingEvents?.data || failedLocalForwardingEvents?.data?.length<1) && apiCallStatus?.status === apiCallStatusEnum.ERROR">{{errorMessage}}</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_event']" [ngClass]="{'display-none': failedLocalForwardingEvents?.data && failedLocalForwardingEvents?.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 *ngIf="errorMessage === ''" [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" class="mb-1"></mat-paginator>
</div>

@ -0,0 +1,53 @@
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { StoreModule } from '@ngrx/store';
import { RootReducer } from '../../../store/rtl.reducers';
import { LNDReducer } from '../../../lnd/store/lnd.reducers';
import { CLReducer } from '../../store/cl.reducers';
import { ECLReducer } from '../../../eclair/store/ecl.reducers';
import { CommonService } from '../../../shared/services/common.service';
import { LoggerService } from '../../../shared/services/logger.service';
import { CLLocalFailedTransactionsComponent } from './local-failed-transactions.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('CLLocalFailedTransactionsComponent', () => {
let component: CLLocalFailedTransactionsComponent;
let fixture: ComponentFixture<CLLocalFailedTransactionsComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [CLLocalFailedTransactionsComponent],
imports: [
BrowserAnimationsModule,
RouterTestingModule,
SharedModule,
StoreModule.forRoot({ root: RootReducer, lnd: LNDReducer, cl: CLReducer, ecl: ECLReducer })
],
providers: [
CommonService,
{ provide: LoggerService, useClass: mockLoggerService },
{ provide: DataService, useClass: mockDataService }
]
}).
compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CLLocalFailedTransactionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
afterEach(() => {
TestBed.resetTestingModule();
});
});

@ -0,0 +1,147 @@
import { Component, OnInit, ViewChild, OnDestroy, AfterViewInit } from '@angular/core';
import { Router } from '@angular/router';
import { DatePipe } from '@angular/common';
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 { LocalFailedEvent } from '../../../shared/models/clModels';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS, getPaginatorLabel, AlertTypeEnum, DataTypeEnum, ScreenSizeEnum, APICallStatusEnum, CLFailReason } 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 { RTLState } from '../../../store/rtl.state';
import { openAlert } from '../../../store/rtl.actions';
import { getLocalFailedForwardingHistory } from '../../store/cl.actions';
import { localFailedForwardingHistory } from '../../store/cl.selector';
@Component({
selector: 'rtl-cl-local-failed-history',
templateUrl: './local-failed-transactions.component.html',
styleUrls: ['./local-failed-transactions.component.scss'],
providers: [
{ provide: MatPaginatorIntl, useValue: getPaginatorLabel('Local Failed Events') }
]
})
export class CLLocalFailedTransactionsComponent implements OnInit, AfterViewInit, OnDestroy {
@ViewChild(MatSort, { static: false }) sort: MatSort | undefined;
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator | undefined;
public CLFailReason = CLFailReason;
public failedLocalEvents: any;
public errorMessage = '';
public displayedColumns: any[] = [];
public failedLocalForwardingEvents: any;
public flgSticky = false;
public selFilter = '';
public pageSize = PAGE_SIZE;
public pageSizeOptions = PAGE_SIZE_OPTIONS;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
public apiCallStatus: ApiCallStatusPayload = null;
public apiCallStatusEnum = APICallStatusEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<RTLState>, private datePipe: DatePipe, private router: Router) {
this.screenSize = this.commonService.getScreenSize();
if (this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['received_time', 'in_channel', 'in_msatoshi', 'actions'];
} else if (this.screenSize === ScreenSizeEnum.SM || this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['received_time', 'in_channel', 'in_msatoshi', 'actions'];
} else {
this.flgSticky = true;
this.displayedColumns = ['received_time', 'in_channel', 'in_msatoshi', 'failreason', 'actions'];
}
}
ngOnInit() {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
this.router.onSameUrlNavigation = 'reload';
this.store.dispatch(getLocalFailedForwardingHistory());
this.store.select(localFailedForwardingHistory).pipe(takeUntil(this.unSubs[0])).
subscribe((lffhSeletor: { localFailedForwardingHistory: LocalFailedEvent[], apiCallStatus: ApiCallStatusPayload }) => {
this.errorMessage = '';
this.apiCallStatus = lffhSeletor.apiCallStatus;
if (this.apiCallStatus.status === APICallStatusEnum.ERROR) {
this.errorMessage = (typeof (this.apiCallStatus.message) === 'object') ? JSON.stringify(this.apiCallStatus.message) : this.apiCallStatus.message;
}
this.failedLocalEvents = lffhSeletor.localFailedForwardingHistory || [];
if (this.failedLocalEvents.length > 0 && this.sort && this.paginator) {
this.loadLocalfailedLocalEventsTable(this.failedLocalEvents);
}
this.logger.info(lffhSeletor);
});
}
ngAfterViewInit() {
if (this.failedLocalEvents.length > 0) {
this.loadLocalfailedLocalEventsTable(this.failedLocalEvents);
}
}
onFailedLocalEventClick(selFEvent: LocalFailedEvent) {
const reorderedFHEvent = [
[{ key: 'received_time', value: selFEvent.received_time, title: 'Received Time', width: 50, type: DataTypeEnum.DATE_TIME },
{ key: 'in_channel_alias', value: selFEvent.in_channel_alias, title: 'Inbound Channel', width: 50, type: DataTypeEnum.STRING }],
[{ key: 'in_msatoshi', value: selFEvent.in_msatoshi, title: 'Amount In (mSats)', width: 100, type: DataTypeEnum.NUMBER }],
[{ key: 'failreason', value: this.CLFailReason[selFEvent.failreason], title: 'Reason for Failure', width: 100, type: DataTypeEnum.STRING }]
];
this.store.dispatch(openAlert({
payload: {
data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Local Failed Event Information',
message: reorderedFHEvent
}
}
}));
}
loadLocalfailedLocalEventsTable(forwardingEvents: LocalFailedEvent[]) {
this.failedLocalForwardingEvents = new MatTableDataSource<LocalFailedEvent>([...forwardingEvents]);
this.failedLocalForwardingEvents.filterPredicate = (event: LocalFailedEvent, fltr: string) => {
const newEvent = ((event.received_time ? this.datePipe.transform(new Date(event.received_time * 1000), 'dd/MMM/YYYY HH:mm').toLowerCase() : '') +
(event.in_channel_alias ? event.in_channel_alias.toLowerCase() : '') +
((event.failreason && this.CLFailReason[event.failreason]) ? this.CLFailReason[event.failreason].toLowerCase() : '') +
(event.in_msatoshi ? (event.in_msatoshi / 1000) : ''));
return newEvent.includes(fltr);
};
this.failedLocalForwardingEvents.sort = this.sort;
this.failedLocalForwardingEvents.sortingDataAccessor = (data: LocalFailedEvent, sortHeaderId: string) => {
switch (sortHeaderId) {
case 'failreason':
return this.CLFailReason[data.failreason];
default:
return (data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : data[sortHeaderId] ? +data[sortHeaderId] : null;
}
};
this.failedLocalForwardingEvents.paginator = this.paginator;
this.applyFilter();
this.logger.info(this.failedLocalForwardingEvents);
}
onDownloadCSV() {
if (this.failedLocalForwardingEvents && this.failedLocalForwardingEvents.data && this.failedLocalForwardingEvents.data.length > 0) {
this.commonService.downloadFile(this.failedLocalForwardingEvents.data, 'Local-failed-transactions');
}
}
applyFilter() {
this.failedLocalForwardingEvents.filter = this.selFilter.trim().toLowerCase();
}
ngOnDestroy() {
this.unSubs.forEach((completeSub) => {
completeSub.next(null);
completeSub.complete();
});
}
}

@ -12,11 +12,11 @@ import { faMapSigns } from '@fortawesome/free-solid-svg-icons';
export class CLRoutingComponent implements OnInit, OnDestroy {
public faMapSigns = faMapSigns;
public links = [{ link: 'forwardinghistory', name: 'Forwarding History' }, { link: 'routingpeers', name: 'Routing Peers' }, { link: 'failedtransactions', name: 'Failed Transactions' }];
public links = [{ link: 'forwardinghistory', name: 'Forwarding History' }, { link: 'routingpeers', name: 'Routing Peers' }, { link: 'failedtransactions', name: 'Failed Transactions' }, { link: 'localfail', name: 'Local Failed Transactions' }];
public activeLink = this.links[0].link;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private router: Router) {}
constructor(private router: Router) { }
ngOnInit() {
const linkFound = this.links.find((link) => this.router.url.includes(link.link));

@ -3,7 +3,7 @@ import { createAction, props } from '@ngrx/store';
import { CLActions } from '../../shared/services/consts-enums-functions';
import { ApiCallStatusPayload } from '../../shared/models/apiCallsPayload';
import { SelNodeChild } from '../../shared/models/RTLconfig';
import { GetInfo, Fees, Peer, Payment, PayRequest, QueryRoutes, Channel, FeeRates, ForwardingEvent, Invoice, ListInvoices, OnChain, UTXO, SaveChannel, GetNewAddress, DetachPeer, UpdateChannel, CloseChannel, DecodePayment, SendPayment, GetQueryRoutes, ChannelLookup, OfferInvoice, Offer, OfferBookmark } from '../../shared/models/clModels';
import { GetInfo, Fees, Peer, Payment, QueryRoutes, Channel, FeeRates, ForwardingEvent, Invoice, ListInvoices, OnChain, UTXO, SaveChannel, GetNewAddress, DetachPeer, UpdateChannel, CloseChannel, SendPayment, GetQueryRoutes, ChannelLookup, OfferInvoice, Offer, OfferBookmark, LocalFailedEvent } from '../../shared/models/clModels';
export const updateCLAPICallStatus = createAction(CLActions.UPDATE_API_CALL_STATUS_CL, props<{ payload: ApiCallStatusPayload }>());
@ -89,6 +89,10 @@ export const getFailedForwardingHistory = createAction(CLActions.GET_FAILED_FORW
export const setFailedForwardingHistory = createAction(CLActions.SET_FAILED_FORWARDING_HISTORY_CL, props<{ payload: ForwardingEvent[] }>());
export const getLocalFailedForwardingHistory = createAction(CLActions.GET_LOCAL_FAILED_FORWARDING_HISTORY_CL);
export const setLocalFailedForwardingHistory = createAction(CLActions.SET_LOCAL_FAILED_FORWARDING_HISTORY_CL, props<{ payload: LocalFailedEvent[] }>());
export const fetchInvoices = createAction(CLActions.FETCH_INVOICES_CL, props<{ payload: { num_max_invoices?: number, index_offset?: number, reversed?: boolean } }>());
export const setInvoices = createAction(CLActions.SET_INVOICES_CL, props<{ payload: ListInvoices }>());

@ -663,17 +663,25 @@ export class CLEffects implements OnDestroy {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
const isNewerVersion = (nodeInfo.api_version) ? this.commonService.isVersionCompatible(nodeInfo.api_version, '0.5.0') : false;
if (!isNewerVersion) {
const filteredLocalFailedEvents = [];
const filteredFailedEvents = [];
const filteredSuccesfulEvents = [];
fhRes.forEach((event: ForwardingEvent) => {
if (event.status === 'settled') {
filteredSuccesfulEvents.push(event);
} else if (event.status === 'failed' || event.status === 'local_failed') {
} else if (event.status === 'failed') {
filteredFailedEvents.push(event);
} else if (event.status === 'local_failed') {
filteredLocalFailedEvents.push(event);
}
});
fhRes = JSON.parse(JSON.stringify(filteredSuccesfulEvents));
this.store.dispatch(setFailedForwardingHistory({ payload: filteredFailedEvents }));
if (action.payload.status === 'failed') {
this.store.dispatch(setFailedForwardingHistory({ payload: filteredFailedEvents }));
}
if (action.payload.status === 'local_failed') {
this.store.dispatch(setFailedForwardingHistory({ payload: filteredLocalFailedEvents }));
}
}
return {
type: CLActions.SET_FORWARDING_HISTORY_CL,
@ -699,21 +707,44 @@ export class CLEffects implements OnDestroy {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFailedForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
return of({ type: RTLActions.VOID });
} // For backwards compatibility < 0.5.0 END
let failedEventsReq = new Observable();
const failedRes = this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=failed');
const localFailedRes = this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=local_failed');
failedEventsReq = forkJoin([failedRes, localFailedRes]);
return failedEventsReq.pipe(map((ffhRes: any) => {
this.logger.info(ffhRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFailedForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLActions.SET_FAILED_FORWARDING_HISTORY_CL,
payload: this.commonService.sortDescByKey([...ffhRes[0], ...ffhRes[1]], 'received_time')
};
}), catchError((err) => {
this.handleErrorWithAlert('FetchFailedForwardingHistory', UI_MESSAGES.NO_SPINNER, 'Get Failed Forwarding History Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=failed', err);
return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=failed').
pipe(map((ffhRes: any) => {
this.logger.info(ffhRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchFailedForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLActions.SET_FAILED_FORWARDING_HISTORY_CL,
payload: ffhRes
};
}), catchError((err) => {
this.handleErrorWithAlert('FetchFailedForwardingHistory', UI_MESSAGES.NO_SPINNER, 'Get Failed Forwarding History Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=failed', err);
return of({ type: RTLActions.VOID });
}));
}))
);
fetchLocalFailedForwardingHistoryCL = createEffect(() => this.actions.pipe(
ofType(CLActions.GET_LOCAL_FAILED_FORWARDING_HISTORY_CL),
withLatestFrom(this.store.select(clNodeInformation)),
mergeMap(([action, nodeInfo]: [{ type: string, payload: any }, GetInfo]) => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalFailedForwardingHistory', status: APICallStatusEnum.INITIATED } }));
// For backwards compatibility < 0.5.0 START
const isNewerVersion = (nodeInfo.api_version) ? this.commonService.isVersionCompatible(nodeInfo.api_version, '0.5.0') : false;
if (!isNewerVersion) {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalFailedForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
return of({ type: RTLActions.VOID });
}));
} // For backwards compatibility < 0.5.0 END
return this.httpClient.get(this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=local_failed').
pipe(map((lffhRes: any) => {
this.logger.info(lffhRes);
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchLocalFailedForwardingHistory', status: APICallStatusEnum.COMPLETED } }));
return {
type: CLActions.SET_LOCAL_FAILED_FORWARDING_HISTORY_CL,
payload: lffhRes
};
}), catchError((err) => {
this.handleErrorWithAlert('FetchLocalFailedForwardingHistory', UI_MESSAGES.NO_SPINNER, 'Get Local Failed Forwarding History Failed', this.CHILD_API_URL + environment.CHANNELS_API + '/listForwards?status=local_failed', err);
return of({ type: RTLActions.VOID });
}));
}))
);

@ -2,7 +2,7 @@ import { createReducer, on } from '@ngrx/store';
import { initCLState } from './cl.state';
import {
addInvoice, addPeer, removeChannel, removePeer, resetCLStore, setBalance, setChannels,
setChildNodeSettingsCL, setFailedForwardingHistory, setFeeRates, setFees, setForwardingHistory,
setChildNodeSettingsCL, setFailedForwardingHistory, setLocalFailedForwardingHistory, setFeeRates, setFees, setForwardingHistory,
setInfo, setInvoices, setLocalRemoteBalance, setOffers, addOffer, setPayments, setPeers, setUTXOs,
updateCLAPICallStatus, updateInvoice, updateOffer, setOfferBookmarks, addUpdateOfferBookmark, removeOfferBookmark
} from './cl.actions';
@ -125,6 +125,14 @@ export const CLReducer = createReducer(initCLState,
failedForwardingHistory: payload
};
}),
on(setLocalFailedForwardingHistory, (state, { payload }) => {
const storedChannels = [...state.activeChannels, ...state.pendingChannels, ...state.inactiveChannels];
payload = mapAliases(payload, storedChannels);
return {
...state,
localFailedForwardingHistory: payload
};
}),
on(addInvoice, (state, { payload }) => {
const newInvoices = state.invoices;
newInvoices.invoices.unshift(payload);
@ -222,13 +230,13 @@ const mapAliases = (payload: any, storedChannels: Channel[]) => {
if (fhEvent.in_channel_alias) { return; }
}
if (idx === storedChannels.length - 1) {
if (!fhEvent.in_channel_alias) { fhEvent.in_channel_alias = fhEvent.in_channel; }
if (!fhEvent.out_channel_alias) { fhEvent.out_channel_alias = fhEvent.out_channel; }
if (!fhEvent.in_channel_alias) { fhEvent.in_channel_alias = fhEvent.in_channel ? fhEvent.in_channel : '-'; }
if (!fhEvent.out_channel_alias) { fhEvent.out_channel_alias = fhEvent.out_channel ? fhEvent.out_channel : '-'; }
}
}
} else {
fhEvent.in_channel_alias = fhEvent.in_channel;
fhEvent.out_channel_alias = fhEvent.out_channel;
fhEvent.in_channel_alias = fhEvent.in_channel ? fhEvent.in_channel : '-';
fhEvent.out_channel_alias = fhEvent.out_channel ? fhEvent.out_channel : '-';
}
});
} else {

@ -1,6 +1,4 @@
import { createFeatureSelector, createSelector, select } from '@ngrx/store';
import { pipe, filter, map, take, find } from 'rxjs';
import { ApiCallStatusPayload } from '../../shared/models/apiCallsPayload';
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { Offer } from '../../shared/models/clModels';
import { CLState } from './cl.state';
@ -21,6 +19,7 @@ export const balance = createSelector(clState, (state: CLState) => ({ balance: s
export const localRemoteBalance = createSelector(clState, (state: CLState) => ({ localRemoteBalance: state.localRemoteBalance, apiCallStatus: state.apisCallStatus.FetchLocalRemoteBalance }));
export const forwardingHistory = createSelector(clState, (state: CLState) => ({ forwardingHistory: state.forwardingHistory, apiCallStatus: state.apisCallStatus.FetchForwardingHistory }));
export const failedForwardingHistory = createSelector(clState, (state: CLState) => ({ failedForwardingHistory: state.failedForwardingHistory, apiCallStatus: state.apisCallStatus.FetchFailedForwardingHistory }));
export const localFailedForwardingHistory = createSelector(clState, (state: CLState) => ({ localFailedForwardingHistory: state.localFailedForwardingHistory, apiCallStatus: state.apisCallStatus.FetchLocalFailedForwardingHistory }));
export const nodeInfoAndNodeSettingsAndBalance = createSelector(clState, (state: CLState) => ({ information: state.information, nodeSettings: state.nodeSettings, balance: state.balance }));
export const nodeInfoAndBalanceAndNumPeers = createSelector(clState, (state: CLState) => ({ information: state.information, balance: state.balance, numPeers: state.peers.length }));
export const nodeInfoAndBalance = createSelector(clState, (state: CLState) => ({ information: state.information, balance: state.balance }));

@ -1,6 +1,6 @@
import { SelNodeChild } from '../../shared/models/RTLconfig';
import { APICallStatusEnum, UserPersonaEnum } from '../../shared/services/consts-enums-functions';
import { GetInfo, Fees, Balance, LocalRemoteBalance, Peer, Payment, Channel, FeeRates, ForwardingEvent, ListInvoices, UTXO, Offer, OfferBookmark } from '../../shared/models/clModels';
import { GetInfo, Fees, Balance, LocalRemoteBalance, Peer, Payment, Channel, FeeRates, ForwardingEvent, ListInvoices, UTXO, Offer, OfferBookmark, LocalFailedEvent } from '../../shared/models/clModels';
import { ApiCallsListCL } from '../../shared/models/apiCallsPayload';
export interface CLState {
@ -19,6 +19,7 @@ export interface CLState {
payments: Payment[];
forwardingHistory: ForwardingEvent[];
failedForwardingHistory: ForwardingEvent[];
localFailedForwardingHistory: LocalFailedEvent[];
invoices: ListInvoices;
utxos: UTXO[];
offers: Offer[];
@ -40,6 +41,7 @@ export const initCLState: CLState = {
FetchPayments: { status: APICallStatusEnum.UN_INITIATED },
FetchForwardingHistory: { status: APICallStatusEnum.UN_INITIATED },
FetchFailedForwardingHistory: { status: APICallStatusEnum.UN_INITIATED },
FetchLocalFailedForwardingHistory: { status: APICallStatusEnum.UN_INITIATED },
FetchOffers: { status: APICallStatusEnum.UN_INITIATED },
FetchOfferBookmarks: { status: APICallStatusEnum.UN_INITIATED }
},
@ -57,6 +59,7 @@ export const initCLState: CLState = {
payments: [],
forwardingHistory: [],
failedForwardingHistory: [],
localFailedForwardingHistory: [],
invoices: { invoices: [] },
utxos: [],
offers: [],

@ -56,6 +56,7 @@ export interface ApiCallsListCL {
FetchPayments: ApiCallStatusPayload;
FetchForwardingHistory: ApiCallStatusPayload;
FetchFailedForwardingHistory: ApiCallStatusPayload;
FetchLocalFailedForwardingHistory: ApiCallStatusPayload;
FetchOffers: ApiCallStatusPayload;
FetchOfferBookmarks: ApiCallStatusPayload;
}

@ -272,6 +272,17 @@ export interface ForwardingEvent {
resolved_time?: number;
}
export interface LocalFailedEvent {
in_channel?: string;
in_channel_alias?: string;
in_msatoshi?: number;
in_msat?: string;
status?: string;
received_time?: number;
failcode?: number;
failreason?: string;
}
export interface Routes {
id?: string;
channel?: string;

@ -72,11 +72,18 @@ export class CommonService implements OnDestroy {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (word.toUpperCase())).replace(/\s+/g, '').replace(/-/g, ' ');
}
titleCase(str) {
titleCase(str: string, searchValue?: string, replaceValue?: string) {
if (searchValue && replaceValue && searchValue !== '' && replaceValue !== '') {
str = str.replace(new RegExp(searchValue, 'g'), replaceValue);
}
if (str.indexOf('!\n') > 0 || str.indexOf('.\n') > 0) {
return str.split('\n').reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + '\n', '');
} else {
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
if (str.indexOf(' ') > 0) {
return str.split(' ').reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + ' ', '');
} else {
return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
}
}
}

@ -146,6 +146,32 @@ export const WALLET_ADDRESS_TYPE = {
UNUSED_NESTED_PUBKEY_HASH: { name: 'Unused Nested Pubkey Hash', tooltip: '' }
};
export enum CLFailReason {
WIRE_INVALID_ONION_VERSION = 'Invalid Onion Version',
WIRE_INVALID_ONION_HMAC = 'Invalid Onion HMAC',
WIRE_INVALID_ONION_KEY = 'Invalid Onion Key',
WIRE_TEMPORARY_CHANNEL_FAILURE = 'Temporary Channel Failure',
WIRE_PERMANENT_CHANNEL_FAILURE = 'Permanent Channel Failure',
WIRE_REQUIRED_CHANNEL_FEATURE_MISSING = 'Missing Required Channel Feature',
WIRE_UNKNOWN_NEXT_PEER = 'Unknown Next Peer',
WIRE_AMOUNT_BELOW_MINIMUM = 'Amount Below Minimum',
WIRE_FEE_INSUFFICIENT = 'Insufficient Fee',
WIRE_INCORRECT_CLTV_EXPIRY = 'Incorrect CLTV Expiry',
WIRE_EXPIRY_TOO_FAR = 'Expiry Too Far',
WIRE_EXPIRY_TOO_SOON = 'Expiry Too Soon',
WIRE_CHANNEL_DISABLED = 'Channel Disabled',
WIRE_INVALID_ONION_PAYLOAD = 'Invalid Onion Payload',
WIRE_INVALID_REALM = 'Invalid Realm',
WIRE_PERMANENT_NODE_FAILURE = 'Permanent Node Failure',
WIRE_TEMPORARY_NODE_FAILURE = 'Temporary Node Failure',
WIRE_REQUIRED_NODE_FEATURE_MISSING = 'Missing Required Node Feature',
WIRE_INVALID_ONION_BLINDING = 'Invalid Onion Binding',
WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS = 'Incorrect or Unknow Payment Details',
WIRE_MPP_TIMEOUT = 'MPP Timeout',
WIRE_FINAL_INCORRECT_CLTV_EXPIRY = 'Incorrect CLTV Expiry',
WIRE_FINAL_INCORRECT_HTLC_AMOUNT = 'Incorrect HTLC Amount'
}
export enum CLChannelPendingState {
CHANNELD_NORMAL = 'Active',
OPENINGD = 'Opening',
@ -457,6 +483,8 @@ export enum CLActions {
SET_FORWARDING_HISTORY_CL = 'SET_FORWARDING_HISTORY_CL',
GET_FAILED_FORWARDING_HISTORY_CL = 'GET_FAILED_FORWARDING_HISTORY_CL',
SET_FAILED_FORWARDING_HISTORY_CL = 'SET_FAILED_FORWARDING_HISTORY_CL',
GET_LOCAL_FAILED_FORWARDING_HISTORY_CL = 'GET_LOCAL_FAILED_FORWARDING_HISTORY_CL',
SET_LOCAL_FAILED_FORWARDING_HISTORY_CL = 'SET_LOCAL_FAILED_FORWARDING_HISTORY_CL',
FETCH_INVOICES_CL = 'FETCH_INVOICES_CL',
SET_INVOICES_CL = 'SET_INVOICES_CL',
SAVE_NEW_INVOICE_CL = 'SAVE_NEW_INVOICE_CL',

Loading…
Cancel
Save