CLT Update payments lists multiple entries for a payment hash (#438)

CLT Update payments lists multiple entries for a payment hash
pull/442/head
ShahanaFarooqui 4 years ago committed by GitHub
parent b50c58a72e
commit c72e2ed2c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -15,5 +15,5 @@
<link rel="stylesheet" href="styles.7f0a84d9b012559f3600.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.fc43c4a60d249539e7ff.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.000a76bc1ed847617608.js" defer></script></body>
<script src="runtime.07b50d065ad9d6662cbb.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.000a76bc1ed847617608.js" defer></script></body>
</html>

@ -0,0 +1 @@
!function(e){function r(r){for(var n,u,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)u=i[p],Object.prototype.hasOwnProperty.call(o,u)&&o[u]&&s.push(o[u][0]),o[u]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return a.push.apply(a,f||[]),t()}function t(){for(var e,r=0;r<a.length;r++){for(var t=a[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(a.splice(r--,1),e=u(u.s=t[0]))}return e}var n={},o={0:0},a=[];function u(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,u),t.l=!0,t.exports}u.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var a,i=document.createElement("script");i.charset="utf-8",i.timeout=120,u.nc&&i.setAttribute("nonce",u.nc),i.src=function(e){return u.p+""+({}[e]||e)+"."+{1:"9bb271dd8dffd2d994a5",6:"ffaa7252647a44b45232",7:"4a00e92294df28ac9ca1",8:"98795e7ab86361a07d2b"}[e]+".js"}(e);var c=new Error;a=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),a=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+a+")",c.name="ChunkLoadError",c.type=n,c.request=a,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){a({type:"timeout",target:i})}),12e4);i.onerror=i.onload=a,document.head.appendChild(i)}return Promise.all(r)},u.m=e,u.c=n,u.d=function(e,r,t){u.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},u.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},u.t=function(e,r){if(1&r&&(e=u(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(u.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)u.d(t,n,(function(r){return e[r]}).bind(null,n));return t},u.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return u.d(r,"a",r),r},u.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},u.p="",u.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

@ -1 +0,0 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"9bb271dd8dffd2d994a5",6:"cc720e644b6f96f3d71b",7:"4a00e92294df28ac9ca1",8:"98795e7ab86361a07d2b"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

@ -3,6 +3,36 @@ var common = require('../../common');
var logger = require('../logger');
var options = {};
function paymentReducer (accumulator, currentPayment) {
let currPayHash = currentPayment.payment_hash;
if(!accumulator[currPayHash]) {
accumulator[currPayHash] = [currentPayment];
} else {
accumulator[currPayHash].push(currentPayment);
}
return accumulator;
}
function groupBy(payments) {
let temp = null;
let paymentsInGroups = payments.reduce(paymentReducer, {});
let paymentsgrpArray = Object.keys(paymentsInGroups).map(key => paymentsInGroups[key]);
return paymentsgrpArray.reduce((acc, curr) => {
if (curr.length && curr.length === 1) {
temp = JSON.parse(JSON.stringify(curr));
temp[0].is_group = false;
temp[0].is_expanded = false;
temp[0].total_parts = 1;
} else {
temp = {};
temp = {is_group: true, is_expanded: false, total_parts: (curr.length ? curr.length : 0), payment_hash: curr[0].payment_hash,
destination: curr[0].destination, msatoshi: curr[0].msatoshi, msatoshi_sent: curr[0].msatoshi_sent, created_at: curr[0].created_at,
created_at_str: curr[0].created_at_str, mpps: curr};
}
return acc.concat(temp);
}, []);
}
exports.listPayments = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/v1/pay/listPayments';
@ -18,10 +48,10 @@ exports.listPayments = (req, res, next) => {
if ( body && body.payments && body.payments.length > 0) {
body.payments.forEach(payment => {
payment.created_at_str = (!payment.created_at) ? '' : common.convertTimestampToDate(payment.created_at);
});
});
body.payments = common.sortDescByKey(body.payments, 'created_at');
}
res.status(200).json(body.payments);
res.status(200).json(groupBy(body.payments));
}
})
.catch(errRes => {

@ -26,14 +26,6 @@
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container w-100">
<mat-progress-bar *ngIf="flgLoading[0]===true" mode="indeterminate"></mat-progress-bar>
<table mat-table #table fxFlex="100" [dataSource]="payments" matSort [ngClass]="{'overflow-auto error-border': flgLoading[0]==='error','overflow-auto': true}">
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>ID</th>
<td mat-cell *matCellDef="let payment">{{payment?.id}}</td>
</ng-container>
<ng-container matColumnDef="bolt11">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Bolt11</th>
<td mat-cell *matCellDef="let payment" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '30rem'}">{{payment?.bolt11}}</td>
</ng-container>
<ng-container matColumnDef="created_at">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Created At</th>
<td mat-cell *matCellDef="let payment">
@ -42,10 +34,6 @@
{{payment?.created_at_str}}
</td>
</ng-container>
<ng-container matColumnDef="destination">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Destination</th>
<td mat-cell *matCellDef="let payment">{{payment?.destination | slice:0:25}}...</td>
</ng-container>
<ng-container matColumnDef="payment_hash">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Payment Hash</th>
<td mat-cell *matCellDef="let payment">
@ -62,20 +50,6 @@
<td mat-cell *matCellDef="let payment"><span
fxLayoutAlign="end center">{{payment?.msatoshi/1000 | number:'1.0-0'}}</span></td>
</ng-container>
<ng-container matColumnDef="payment_preimage">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Payment Pre Image</th>
<td mat-cell *matCellDef="let payment">
<div>{{payment?.payment_preimage | slice:0:10}}<span *ngIf="payment?.payment_preimage">...</span></div>
</td>
</ng-container>
<ng-container matColumnDef="amount_msat">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Amount mSat</th>
<td mat-cell *matCellDef="let payment">{{payment?.amount_msat}}</td>
</ng-container>
<ng-container matColumnDef="amount_sent_msat">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Amount Sent mSat</th>
<td mat-cell *matCellDef="let payment">{{payment?.amount_sent_msat}}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="px-3">
<div class="bordered-box table-actions-select">
@ -86,17 +60,75 @@
</div>
</th>
<td mat-cell *matCellDef="let payment" class="px-3" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onPaymentClick(payment,$event)">View Info</button>
<button mat-stroked-button color="primary" type="button" tabindex="4" (click)="onPaymentClick(payment)">View Info</button>
</td>
</ng-container>
<ng-container matColumnDef="no_payment">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!payments.data || payments.data.length<1">No payments available.</p>
<p *ngIf="!payments?.data || payments?.data?.length<1">No payments available.</p>
</td>
</ng-container>
<!-- Payment Group Row Start -->
<ng-container matColumnDef="groupTotal">
<td mat-cell *matCellDef="let payment">
<span fxLayoutAlign="start center" class="mpp-row-span">Total Attempts: {{payment?.total_parts}}</span>
<ng-container *ngIf="payment.is_expanded">
<span *ngFor="let mpp of payment?.mpps" fxLayoutAlign="start center" class="mpp-row-span">
<span *ngIf="mpp.status === 'complete'" class="dot green" matTooltip="Completed" matTooltipPosition="right" [ngClass]="{'mr-0': screenSize === screenSizeEnum.XS}"></span>
<span *ngIf="mpp.status !== 'complete'" class="dot yellow" matTooltip="Incomplete" matTooltipPosition="right" [ngClass]="{'mr-0': screenSize === screenSizeEnum.XS}"></span>
{{mpp.created_at_str}}
</span>
</ng-container>
</td>
</ng-container>
<ng-container matColumnDef="groupHash">
<td mat-cell *matCellDef="let payment">
<span fxLayoutAlign="start center" class="mpp-row-span">{{payment?.payment_hash}}</span>
<span *ngIf="payment.is_expanded">
<span *ngFor="let mpp of payment?.mpps" fxLayoutAlign="start center" class="mpp-row-span">
Part ID {{mpp.partid ? mpp.partid : 0}}
</span>
</span>
</td>
</ng-container>
<ng-container matColumnDef="groupAmtSent">
<td mat-cell *matCellDef="let payment">
<span fxLayoutAlign="end center" class="mpp-row-span">{{payment?.msatoshi_sent/1000 | number:'1.0-0'}}</span>
<span *ngIf="payment.is_expanded">
<span *ngFor="let mpp of payment?.mpps" fxLayoutAlign="end center" class="mpp-row-span">
{{mpp.msatoshi_sent/1000 | number:'1.0-0'}}
</span>
</span>
</td>
</ng-container>
<ng-container matColumnDef="groupAmtRecv">
<td mat-cell *matCellDef="let payment">
<span fxLayoutAlign="end center" class="mpp-row-span">{{payment?.msatoshi/1000 | number:'1.0-0'}}</span>
<span *ngIf="payment.is_expanded">
<span *ngFor="let mpp of payment?.mpps" fxLayoutAlign="end center" class="mpp-row-span">
{{mpp.msatoshi/1000 | number:'1.0-0'}}
</span>
</span>
</td>
</ng-container>
<ng-container matColumnDef="groupAction">
<td mat-cell *matCellDef="let payment" class="px-3">
<span fxLayoutAlign="end center">
<button mat-stroked-button class="btn-mpp-expand" color="primary" type="button" tabindex="5" (click)="payment.is_expanded = !payment.is_expanded">{{payment.is_expanded ? 'Hide Attemps' : 'Show Attemps'}}</button>
</span>
<div *ngIf="payment.is_expanded">
<div *ngFor="let mpp of payment?.mpps; index as i" fxLayoutAlign="end center">
<button mat-stroked-button class="btn-mpp-info" color="primary" type="button" tabindex="6" (click)="onPaymentClick(mpp)">View Part {{mpp.partid ? mpp.partid : 0}}</button>
</div>
</div>
</td>
</ng-container>
<tr mat-row *matRowDef="let row; columns: mppColumns; when: is_group;" [@newlyAddedRowAnimation]="(row.payment_hash === newlyAddedPayment && flgAnimate) ? 'added' : 'notAdded'"></tr>
<!-- Payment Group Row End -->
<tr mat-footer-row *matFooterRowDef="['no_payment']" [ngClass]="{'display-none': payments.data && payments.data.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [@newlyAddedRowAnimation]="(row.payment_hash === newlyAddedPayment && flgAnimate) ? 'added' : 'notAdded'"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns; when: !is_group;" [@newlyAddedRowAnimation]="(row.payment_hash === newlyAddedPayment && flgAnimate) ? 'added' : 'notAdded'"></tr>
</table>
</div>
<mat-paginator [pageSize]="pageSize" [pageSizeOptions]="pageSizeOptions" [showFirstLastButtons]="screenSize === screenSizeEnum.XS ? false : true" class="mb-4"></mat-paginator>

@ -8,3 +8,15 @@
.mat-column-actions {
min-height: 4.8rem;
}
.mat-column-groupAction {
min-height: 4.8rem;
& .btn-mpp-info {
margin-top: 0.5rem;
}
}
.mpp-row-span {
min-height: 4.2rem;
}

@ -45,6 +45,7 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
public payments: any;
public paymentJSONArr: Payment[] = [];
public displayedColumns = [];
public mppColumns = [];
public paymentDecoded: PayRequest = {};
public paymentRequest = '';
public paymentDecodedHint = '';
@ -60,15 +61,19 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['created_at', 'actions'];
this.mppColumns = ['groupTotal', 'groupAction'];
} else if(this.screenSize === ScreenSizeEnum.SM) {
this.flgSticky = false;
this.displayedColumns = ['created_at', 'msatoshi', 'actions'];
this.mppColumns = ['groupTotal', 'groupAmtRecv', 'groupAction'];
} else if(this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['created_at', 'msatoshi_sent', 'msatoshi', 'actions'];
this.mppColumns = ['groupTotal', 'groupAmtSent', 'groupAmtRecv', 'groupAction'];
} else {
this.flgSticky = true;
this.displayedColumns = ['created_at', 'payment_hash', 'msatoshi_sent', 'msatoshi', 'actions'];
this.mppColumns = ['groupTotal', 'groupHash', 'groupAmtSent', 'groupAmtRecv', 'groupAction'];
}
}
@ -96,7 +101,10 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
}
this.logger.info(rtlStore);
});
}
is_group(index: number, payment: Payment) {
return payment.is_group;
}
onSendPayment() {
@ -224,10 +232,9 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
this.form.resetForm();
}
onPaymentClick(selPayment: Payment, event: any) {
onPaymentClick(selPayment) {
const reorderedPayment = [
[{key: 'bolt11', value: selPayment.bolt11, title: 'Bolt 11', width: 100, type: DataTypeEnum.STRING}],
[{key: 'payment_hash', value: selPayment.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING}],
[{key: 'payment_preimage', value: selPayment.payment_preimage, title: 'Payment Preimage', width: 100, type: DataTypeEnum.STRING}],
[{key: 'id', value: selPayment.id, title: 'ID', width: 20, type: DataTypeEnum.STRING},
{key: 'destination', value: selPayment.destination, title: 'Destination', width: 80, type: DataTypeEnum.STRING}],
@ -236,6 +243,13 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
[{key: 'msatoshi', value: selPayment.msatoshi, title: 'Amount (mSats)', width: 50, type: DataTypeEnum.NUMBER},
{key: 'msatoshi_sent', value: selPayment.msatoshi_sent, title: 'Amount Sent (mSats)', width: 50, type: DataTypeEnum.NUMBER}]
];
if (selPayment.partid) {
reorderedPayment.unshift(
[{key: 'payment_hash', value: selPayment.payment_hash, title: 'Payment Hash', width: 80, type: DataTypeEnum.STRING},
{key: 'partid', value: selPayment.partid, title: 'Part ID', width: 20, type: DataTypeEnum.STRING}]);
} else {
reorderedPayment.unshift([{key: 'payment_hash', value: selPayment.payment_hash, title: 'Payment Hash', width: 100, type: DataTypeEnum.STRING}]);
}
this.store.dispatch(new RTLActions.OpenAlert({ data: {
type: AlertTypeEnum.INFORMATION,
alertTitle: 'Payment Information',
@ -249,7 +263,18 @@ export class CLLightningPaymentsComponent implements OnInit, OnDestroy {
onDownloadCSV() {
if(this.payments.data && this.payments.data.length > 0) {
this.commonService.downloadFile(this.payments.data, 'Payments');
let paymentsDataCopy = JSON.parse(JSON.stringify(this.payments.data));
let flattenedPayments = paymentsDataCopy.reduce((acc, curr) => {
if (curr.mpps) {
return acc.concat(curr.mpps);
} else {
delete curr.is_group;
delete curr.is_expanded;
delete curr.total_parts;
return acc.concat(curr);
}
}, []);
this.commonService.downloadFile(flattenedPayments, 'Payments');
}
}

@ -138,6 +138,26 @@ export interface Payment {
payment_hash?: string;
payment_preimage?: string;
status?: string;
is_group?: boolean;
is_expanded?: boolean;
total_parts?: number;
mpps?: MPP[];
}
export interface MPP {
amount_msat?: string;
amount_sent_msat?: string;
bolt11?: string;
created_at?: number;
created_at_str?: string;
destination?: string;
id?: number;
msatoshi?: number;
msatoshi_sent?: number;
payment_hash?: string;
payment_preimage?: string;
status?: string;
partid?: number;
}
export interface PayRequest {

Loading…
Cancel
Save