Fee rate numeric specs #676 (#762)

Fee rate numeric specs #676
pull/763/head
ShahanaFarooqui 3 years ago committed by GitHub
parent 6cf292f401
commit 13ea5389a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

File diff suppressed because one or more lines are too long

@ -10,9 +10,9 @@
<link i18n-rel="" rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
<meta i18n-content="" name="msapplication-TileColor" content="#da532c">
<meta i18n-content="" name="theme-color" content="#ffffff">
<style>@font-face{font-family:Roboto;src:url(Roboto-Thin.dbd56bd3357dc3617fe5.woff2) format("woff2"),url(Roboto-Thin.e7f7c82374bd0ebef14b.woff) format("woff");font-weight:100;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.a8cef84f735ef887abdc.woff2) format("woff2"),url(Roboto-ThinItalic.5dd9349c940073834e9a.woff) format("woff");font-weight:100;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Light.c27d89ac77468ae18f28.woff2) format("woff2"),url(Roboto-Light.d923dfafc0c5183b59aa.woff) format("woff");font-weight:300;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.506274c7228cf81cae4d.woff2) format("woff2"),url(Roboto-LightItalic.d4b8c137518d9d92bb28.woff) format("woff");font-weight:300;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Regular.64cfb66c866ea50cad47.woff2) format("woff2"),url(Roboto-Regular.e02e9d6ff5547f7e9962.woff) format("woff");font-weight:400;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.4dd2af1e8df532f41db8.woff2) format("woff2"),url(Roboto-RegularItalic.5ea38fff9eebef99c5df.woff) format("woff");font-weight:400;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Medium.1d3bced88509b0838984.woff2) format("woff2"),url(Roboto-Medium.092c6130df8fd2199888.woff) format("woff");font-weight:500;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.d620b8f53f75966fe42e.woff2) format("woff2"),url(Roboto-MediumItalic.18ff1628c628080166c1.woff) format("woff");font-weight:500;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Bold.92fbd4e93cf0a5dbebaa.woff2) format("woff2"),url(Roboto-Bold.73288d91c325e82a5b92.woff) format("woff");font-weight:700;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.5f600d98a73d800ae575.woff2) format("woff2"),url(Roboto-BoldItalic.6d89acbd21d7e3fbecb2.woff) format("woff");font-weight:700;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Black.41ed1105a6ebb8ffe34e.woff2) format("woff2"),url(Roboto-Black.937491dfcbe64ca9a9f1.woff) format("woff");font-weight:900;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.50ca4c51ebc27e7e7d2f.woff2) format("woff2"),url(Roboto-BlackItalic.2e1ee657996854c6f427.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;margin:0;}body{height:100%;overflow:hidden;}*{margin:0;padding:0;}</style><link rel="stylesheet" href="styles.08f05775866285e5b944.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.08f05775866285e5b944.css"></noscript></head>
<style>@font-face{font-family:Roboto;src:url(Roboto-Thin.dbd56bd3357dc3617fe5.woff2) format("woff2"),url(Roboto-Thin.e7f7c82374bd0ebef14b.woff) format("woff");font-weight:100;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.a8cef84f735ef887abdc.woff2) format("woff2"),url(Roboto-ThinItalic.5dd9349c940073834e9a.woff) format("woff");font-weight:100;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Light.c27d89ac77468ae18f28.woff2) format("woff2"),url(Roboto-Light.d923dfafc0c5183b59aa.woff) format("woff");font-weight:300;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.506274c7228cf81cae4d.woff2) format("woff2"),url(Roboto-LightItalic.d4b8c137518d9d92bb28.woff) format("woff");font-weight:300;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Regular.64cfb66c866ea50cad47.woff2) format("woff2"),url(Roboto-Regular.e02e9d6ff5547f7e9962.woff) format("woff");font-weight:400;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.4dd2af1e8df532f41db8.woff2) format("woff2"),url(Roboto-RegularItalic.5ea38fff9eebef99c5df.woff) format("woff");font-weight:400;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Medium.1d3bced88509b0838984.woff2) format("woff2"),url(Roboto-Medium.092c6130df8fd2199888.woff) format("woff");font-weight:500;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.d620b8f53f75966fe42e.woff2) format("woff2"),url(Roboto-MediumItalic.18ff1628c628080166c1.woff) format("woff");font-weight:500;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Bold.92fbd4e93cf0a5dbebaa.woff2) format("woff2"),url(Roboto-Bold.73288d91c325e82a5b92.woff) format("woff");font-weight:700;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.5f600d98a73d800ae575.woff2) format("woff2"),url(Roboto-BoldItalic.6d89acbd21d7e3fbecb2.woff) format("woff");font-weight:700;font-style:italic;}@font-face{font-family:Roboto;src:url(Roboto-Black.41ed1105a6ebb8ffe34e.woff2) format("woff2"),url(Roboto-Black.937491dfcbe64ca9a9f1.woff) format("woff");font-weight:900;font-style:normal;}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.50ca4c51ebc27e7e7d2f.woff2) format("woff2"),url(Roboto-BlackItalic.2e1ee657996854c6f427.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;margin:0;}body{height:100%;overflow:hidden;}*{margin:0;padding:0;}</style><link rel="stylesheet" href="styles.8a7bba95e633f72b8334.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.8a7bba95e633f72b8334.css"></noscript></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.685250420a13de37bff1.js" defer></script><script src="polyfills.a979cbbe16939013cdcf.js" defer></script><script src="main.ffe6d8aeb31791ae0a01.js" defer></script>
<script src="runtime.2e282cc6f811209b3099.js" defer></script><script src="polyfills.a979cbbe16939013cdcf.js" defer></script><script src="main.1e9a1ea238c1e1f66199.js" defer></script>
</body></html>

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
(()=>{"use strict";var e,r,t,o={},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var l=1/0;for(c=0;c<e.length;c++){for(var[t,o,a]=e[c],i=!0,d=0;d<t.length;d++)(!1&a||l>=a)&&Object.keys(n.O).every(e=>n.O[e](t[d]))?t.splice(d--,1):(i=!1,a<l&&(l=a));i&&(e.splice(c--,1),r=o())}return r}a=a||0;for(var c=e.length;c>0&&e[c-1][2]>a;c--)e[c]=e[c-1];e[c]=[t,o,a]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+"."+{358:"c23c0f6205af83b3d6c4",432:"edc184470c776a31dec4",891:"5f9b365bcec35211e41e",958:"a55d8156c48e68ddc825"}[e]+".js",n.miniCssF=e=>"styles.8a7bba95e633f72b8334.css",n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="rtl:",n.l=(e,o,a,l)=>{if(r[e])r[e].push(o);else{var i,d;if(void 0!==a)for(var c=document.getElementsByTagName("script"),s=0;s<c.length;s++){var u=c[s];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+a){i=u;break}}i||(d=!0,(i=document.createElement("script")).charset="utf-8",i.timeout=120,n.nc&&i.setAttribute("nonce",n.nc),i.setAttribute("data-webpack",t+a),i.src=e),r[e]=[o];var f=(t,o)=>{i.onerror=i.onload=null,clearTimeout(p);var a=r[e];if(delete r[e],i.parentNode&&i.parentNode.removeChild(i),a&&a.forEach(e=>e(o)),t)return t(o)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=f.bind(null,i.onerror),i.onload=f.bind(null,i.onload),d&&document.head.appendChild(i)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),n.p="",(()=>{var e={666:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else if(666!=r){var a=new Promise((t,a)=>o=e[r]=[t,a]);t.push(o[2]=a);var l=n.p+n.u(r),i=new Error;n.l(l,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),l=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+a+": "+l+")",i.name="ChunkLoadError",i.type=a,i.request=l,o[1](i)}},"chunk-"+r,r)}else e[r]=0},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[l,i,d]=t,c=0;for(o in i)n.o(i,o)&&(n.m[o]=i[o]);if(d)var s=d(n);for(r&&r(t);c<l.length;c++)n.o(e,a=l[c])&&e[a]&&e[a][0](),e[l[c]]=0;return n.O(s)},t=self.webpackChunkrtl=self.webpackChunkrtl||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})()})();

@ -1 +0,0 @@
(()=>{"use strict";var e,r,t,o={},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={id:e,loaded:!1,exports:{}};return o[e].call(t.exports,t,t.exports,n),t.loaded=!0,t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var l=1/0;for(s=0;s<e.length;s++){for(var[t,o,a]=e[s],d=!0,i=0;i<t.length;i++)(!1&a||l>=a)&&Object.keys(n.O).every(e=>n.O[e](t[i]))?t.splice(i--,1):(d=!1,a<l&&(l=a));d&&(e.splice(s--,1),r=o())}return r}a=a||0;for(var s=e.length;s>0&&e[s-1][2]>a;s--)e[s]=e[s-1];e[s]=[t,o,a]},n.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return n.d(r,{a:r}),r},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce((r,t)=>(n.f[t](e,r),r),[])),n.u=e=>e+"."+{358:"77037545e0b6cfdc05f9",432:"edc184470c776a31dec4",891:"5f9b365bcec35211e41e",958:"a55d8156c48e68ddc825"}[e]+".js",n.miniCssF=e=>"styles.08f05775866285e5b944.css",n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="rtl:",n.l=(e,o,a,l)=>{if(r[e])r[e].push(o);else{var d,i;if(void 0!==a)for(var s=document.getElementsByTagName("script"),c=0;c<s.length;c++){var u=s[c];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==t+a){d=u;break}}d||(i=!0,(d=document.createElement("script")).charset="utf-8",d.timeout=120,n.nc&&d.setAttribute("nonce",n.nc),d.setAttribute("data-webpack",t+a),d.src=e),r[e]=[o];var f=(t,o)=>{d.onerror=d.onload=null,clearTimeout(p);var a=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),a&&a.forEach(e=>e(o)),t)return t(o)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=f.bind(null,d.onerror),d.onload=f.bind(null,d.onload),i&&document.head.appendChild(d)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),n.p="",(()=>{var e={666:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else if(666!=r){var a=new Promise((t,a)=>o=e[r]=[t,a]);t.push(o[2]=a);var l=n.p+n.u(r),d=new Error;n.l(l,t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),l=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+a+": "+l+")",d.name="ChunkLoadError",d.type=a,d.request=l,o[1](d)}},"chunk-"+r,r)}else e[r]=0},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[l,d,i]=t,s=0;for(o in d)n.o(d,o)&&(n.m[o]=d[o]);if(i)var c=i(n);for(r&&r(t);s<l.length;s++)n.o(e,a=l[s])&&e[a]&&e[a][0](),e[l[s]]=0;return n.O(c)},t=self.webpackChunkrtl=self.webpackChunkrtl||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})()})();

File diff suppressed because one or more lines are too long

@ -39,23 +39,29 @@
</mat-panel-title>
</mat-expansion-panel-header>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" [(value)]="selFeeRate" [disabled]="flgMinConf">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap">
<div fxFlex="48" fxLayoutAlign="space-between end">
<mat-form-field [fxFlex]="selFeeRate === 'customperkb' && !flgMinConf ? '48' : '100'" fxLayoutAlign="start center">
<mat-select tabindex="4" placeholder="Fee Rate" [(value)]="selFeeRate" (selectionChange)="customFeeRate=null" [disabled]="flgMinConf">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="selFeeRate === 'customperkb' && !flgMinConf" fxFlex="48" fxLayoutAlign="end center">
<input matInput [(ngModel)]="customFeeRate" placeholder="Fee Rate (Sats/vB)" type="number" name="custFeeRate" [step]="0.1" [min]="0" tabindex="4" #custFeeRate="ngModel" [required]="selFeeRate === 'customperkb' && !flgMinConf">
<mat-error *ngIf="selFeeRate === 'customperkb' && !flgMinConf && !customFeeRate">Fee Rate is required.</mat-error>
</mat-form-field>
</div>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-checkbox fxFlex="2" tabindex="5" color="primary" [(ngModel)]="flgMinConf" (change)="flgMinConf ? selFeeRate=null : minConfValue=null" name="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput [(ngModel)]="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" [step]="1" [min]="0" tabindex="8" #blocks="ngModel" [required]="flgMinConf" [disabled]="!flgMinConf">
<mat-error *ngIf="flgMinConf && !minConfValue">Min Confirmation Blocks is required.</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center" *ngIf="isCompatibleVersion">
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayoutAlign.gt-sm="space-between center" fxLayout.gt-sm="row wrap" *ngIf="isCompatibleVersion">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="6" placeholder="Coin Selection" (selectionChange)="onUTXOSelectionChange($event)" [(value)]="selUTXOs" multiple>
<mat-select-trigger>{{totalSelectedUTXOAmount | number}} Sats ({{selUTXOs.length > 1 ? selUTXOs.length + ' UTXOs' : '1 UTXO'}})</mat-select-trigger>

@ -2,9 +2,11 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { StoreModule } from '@ngrx/store';
import { CommonService } from '../../../../shared/services/common.service';
import { DataService } from '../../../../shared/services/data.service';
import { RTLReducer } from '../../../../store/rtl.reducers';
import { CLOpenChannelComponent } from './open-channel.component';
import { mockCLEffects, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../../shared/test-helpers/mock-services';
import { mockCLEffects, mockDataService, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../../shared/test-helpers/mock-services';
import { EffectsModule } from '@ngrx/effects';
import { SharedModule } from '../../../../shared/shared.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -28,6 +30,8 @@ describe('CLOpenChannelComponent', () => {
EffectsModule.forRoot([mockRTLEffects, mockLNDEffects, mockCLEffects, mockECLEffects])
],
providers: [
CommonService,
{ provide: DataService, useClass: mockDataService },
{ provide: MatDialogRef, useClass: mockMatDialogRef },
{ provide: MAT_DIALOG_DATA, useValue: { message: {} } }
]

@ -8,9 +8,10 @@ import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { CommonService } from '../../../../shared/services/common.service';
import { Peer, GetInfo, UTXO } from '../../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../../shared/models/alertData';
import { APICallStatusEnum, FEE_RATE_TYPES } from '../../../../shared/services/consts-enums-functions';
import { APICallStatusEnum, FEE_RATE_TYPES, ScreenSizeEnum } from '../../../../shared/services/consts-enums-functions';
import * as CLActions from '../../../store/cl.actions';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@ -44,11 +45,16 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
public isPrivate = false;
public feeRateTypes = FEE_RATE_TYPES;
public selFeeRate = '';
public customFeeRate = null;
public flgMinConf = false;
public minConfValue = null;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private actions: Actions, private decimalPipe: DecimalPipe) { }
constructor(public dialogRef: MatDialogRef<CLOpenChannelComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private actions: Actions, private decimalPipe: DecimalPipe, private commonService: CommonService) {
this.screenSize = this.commonService.getScreenSize();
}
ngOnInit() {
this.isCompatibleVersion = this.data.message.isCompatibleVersion;
@ -132,7 +138,7 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
this.advancedTitle = this.advancedTitle + ' | Min Confirmation Blocks: ' + this.minConfValue;
}
if (this.selFeeRate) {
this.advancedTitle = this.advancedTitle + ' | Fee Rate: ' + this.feeRateTypes.find((feeRateType) => feeRateType.feeRateId === this.selFeeRate).feeRateType;
this.advancedTitle = this.advancedTitle + ' | Fee Rate: ' + (this.customFeeRate ? (this.customFeeRate + ' (Sats/vB)') : (this.feeRateTypes.find((feeRateType) => feeRateType.feeRateId === this.selFeeRate).feeRateType));
}
if (this.selUTXOs.length && this.selUTXOs.length > 0) {
this.advancedTitle = this.advancedTitle + ' | Total Selected: ' + this.selUTXOs.length + ' | Selected UTXOs: ' + this.decimalPipe.transform(this.totalSelectedUTXOAmount) + ' Sats';
@ -169,10 +175,11 @@ export class CLOpenChannelComponent implements OnInit, OnDestroy {
}
onOpenChannel(): boolean | void {
if ((!this.peer && !this.selectedPubkey) || (!this.fundingAmount || ((this.totalBalance - this.fundingAmount) < 0) || (this.flgMinConf && !this.minConfValue))) {
if ((!this.peer && !this.selectedPubkey) || (!this.fundingAmount || ((this.totalBalance - this.fundingAmount) < 0) || (this.flgMinConf && !this.minConfValue)) || (this.selFeeRate === 'customperkb' && !this.flgMinConf && !this.customFeeRate)) {
return true;
}
const newChannel = { peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: (this.flgUseAllBalance) ? 'all' : this.fundingAmount.toString(), announce: !this.isPrivate, feeRate: this.selFeeRate, minconf: this.flgMinConf ? this.minConfValue : null };
const newChannel = { peerId: ((!this.peer || !this.peer.id) ? this.selectedPubkey : this.peer.id), satoshis: (this.flgUseAllBalance) ? 'all' : this.fundingAmount.toString(), announce: !this.isPrivate, minconf: this.flgMinConf ? this.minConfValue : null };
newChannel['feeRate'] = (this.selFeeRate === 'customperkb' && !this.flgMinConf && this.customFeeRate) ? (this.customFeeRate * 1000) + 'perkb' : this.selFeeRate;
if (this.selUTXOs.length && this.selUTXOs.length > 0) {
newChannel['utxos'] = [];
this.selUTXOs.forEach((utxo) => newChannel['utxos'].push(utxo.txid + ':' + utxo.output));

@ -26,10 +26,10 @@
</form>
</mat-step>
<mat-step [stepControl]="channelFormGroup" [editable]="flgEditable">
<form [formGroup]="channelFormGroup" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between" class="mb-1">
<form [formGroup]="channelFormGroup" fxLayout="column" fxLayout.gt-sm="row wrap" fxLayoutAlign="start" fxLayoutAlign.gt-sm="space-between" class="mb-1" (ngSubmit)="onOpenChannel()">
<ng-template matStepLabel disabled="true">{{channelFormLabel}}</ng-template>
<div fxLayout="column" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign="space-between stretch">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start stretch">
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row" fxFlex="100" fxLayoutAlign.gt-sm="space-between center">
<mat-form-field fxFlex="60" fxLayoutAlign="start end">
<input matInput autoFocus formControlName="fundingAmount" placeholder="Amount" type="number" [step]="1000" tabindex="1" required>
<mat-hint>Remaining Bal: {{totalBalance - ((channelFormGroup.controls.fundingAmount.value) ? channelFormGroup.controls.fundingAmount.value : 0)}}</mat-hint>
@ -42,16 +42,22 @@
<mat-slide-toggle tabindex="2" color="primary" formControlName="isPrivate" name="isPrivate">Private Channel</mat-slide-toggle>
</div>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center" class="mt-1">
<mat-form-field fxFlex="48" fxLayoutAlign="start end">
<mat-select tabindex="4" placeholder="Fee Rate" formControlName="selFeeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" fxFlex="100" fxLayoutAlign.gt-sm="space-between center">
<div fxFlex="48" fxLayoutAlign="space-between end">
<mat-form-field [fxFlex]="channelFormGroup.controls.selFeeRate.value === 'customperkb' && !channelFormGroup.controls.flgMinConf.value ? '48' : '100'" fxLayoutAlign="start center">
<mat-select tabindex="4" placeholder="Fee Rate" formControlName="selFeeRate">
<mat-option *ngFor="let feeRateType of feeRateTypes" [value]="feeRateType.feeRateId">
{{feeRateType.feeRateType}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field *ngIf="channelFormGroup.controls.selFeeRate.value === 'customperkb' && !channelFormGroup.controls.flgMinConf.value" fxFlex="48" fxLayoutAlign="end center">
<input matInput formControlName="customFeeRate" placeholder="Fee Rate (Sats/vB)" type="number" name="custFeeRate" [step]="0.1" [min]="0" tabindex="4">
<mat-error *ngIf="channelFormGroup.controls.selFeeRate.value === 'customperkb' && !channelFormGroup.controls.flgMinConf.value && !channelFormGroup.controls.customFeeRate.value">Fee Rate is required.</mat-error>
</mat-form-field>
</div>
<div fxFlex="48" fxLayout="row" fxLayoutAlign="start center">
<mat-checkbox fxFlex="2" tabindex="5" color="primary" formControlName="flgMinConf" name="flgMinConf" fxLayoutAlign="stretch start" class="mr-2"></mat-checkbox>
<mat-checkbox fxFlex="2" tabindex="5" color="primary" formControlName="flgMinConf" fxLayoutAlign="stretch start" [ngClass]="{'mr-6': screenSize === screenSizeEnum.XS || screenSize === screenSizeEnum.SM, 'mr-2': screenSize === screenSizeEnum.MD || screenSize === screenSizeEnum.LG || screenSize === screenSizeEnum.XL}"></mat-checkbox>
<mat-form-field fxFlex="98">
<input matInput formControlName="minConfValue" placeholder="Min Confirmation Blocks" type="number" name="blocks" [step]="1" [min]="0" tabindex="8" [required]="channelFormGroup.controls.flgMinConf.value">
<mat-error *ngIf="channelFormGroup.controls.flgMinConf.value && !channelFormGroup.controls.minConfValue.value">Min Confirmation Blocks is required.</mat-error>
@ -64,7 +70,7 @@
<span>{{channelConnectionError}}</span>
</div>
<div class="mt-2" fxLayout="row" fxLayoutAlign="start center" fxFlex="100">
<button mat-stroked-button color="primary" tabindex="8" type="button" (click)="onOpenChannel()">{{channelConnectionError !== '' ? 'Retry' : 'Open Channel'}}</button>
<button mat-stroked-button color="primary" tabindex="8" type="submit">{{channelConnectionError !== '' ? 'Retry' : 'Open Channel'}}</button>
</div>
</form>
</mat-step>

@ -5,9 +5,11 @@ import { StoreModule } from '@ngrx/store';
import { RTLReducer } from '../../../store/rtl.reducers';
import { SharedModule } from '../../../shared/shared.module';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
import { DataService } from '../../../shared/services/data.service';
import { CLConnectPeerComponent } from './connect-peer.component';
import { mockCLEffects, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../shared/test-helpers/mock-services';
import { mockCLEffects, mockDataService, mockECLEffects, mockLNDEffects, mockMatDialogRef, mockRTLEffects } from '../../../shared/test-helpers/mock-services';
import { EffectsModule } from '@ngrx/effects';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
@ -30,7 +32,8 @@ describe('CLConnectPeerComponent', () => {
EffectsModule.forRoot([mockRTLEffects, mockLNDEffects, mockCLEffects, mockECLEffects])
],
providers: [
LoggerService,
LoggerService, CommonService,
{ provide: DataService, useClass: mockDataService },
{ provide: MatDialogRef, useClass: mockMatDialogRef },
{ provide: MAT_DIALOG_DATA, useValue: { alertTitle: '', titleMessage: '', message: {}, newlyAdded: true } }
]

@ -8,10 +8,11 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatStepper } from '@angular/material/stepper';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { CommonService } from '../../../shared/services/common.service';
import { LoggerService } from '../../../shared/services/logger.service';
import { Peer } from '../../../shared/models/clModels';
import { CLOpenChannelAlert } from '../../../shared/models/alertData';
import { APICallStatusEnum, FEE_RATE_TYPES } from '../../../shared/services/consts-enums-functions';
import { APICallStatusEnum, FEE_RATE_TYPES, ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
import * as CLActions from '../../store/cl.actions';
import * as fromRTLReducer from '../../../store/rtl.reducers';
@ -37,12 +38,16 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
public channelConnectionError = '';
public peerFormLabel = 'Peer Details';
public channelFormLabel = 'Open Channel (Optional)';
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
peerFormGroup: FormGroup;
channelFormGroup: FormGroup;
statusFormGroup: FormGroup;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<CLConnectPeerComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private formBuilder: FormBuilder, private actions: Actions, private logger: LoggerService) {}
constructor(public dialogRef: MatDialogRef<CLConnectPeerComponent>, @Inject(MAT_DIALOG_DATA) public data: CLOpenChannelAlert, private store: Store<fromRTLReducer.RTLState>, private formBuilder: FormBuilder, private actions: Actions, private logger: LoggerService, private commonService: CommonService) {
this.screenSize = this.commonService.getScreenSize();
}
ngOnInit() {
this.totalBalance = this.data.message.balance;
@ -56,8 +61,9 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
fundingAmount: ['', [Validators.required, Validators.min(1), Validators.max(this.totalBalance)]],
isPrivate: [false],
selFeeRate: [null],
customFeeRate: [null],
flgMinConf: [false],
minConfValue: [null],
minConfValue: [{ value: null, disabled: true }],
hiddenAmount: ['', [Validators.required]]
});
this.statusFormGroup = this.formBuilder.group({});
@ -65,16 +71,28 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
if (flg) {
this.channelFormGroup.controls.selFeeRate.setValue(null);
this.channelFormGroup.controls.selFeeRate.disable();
this.channelFormGroup.controls.customFeeRate.setValue(null);
this.channelFormGroup.controls.minConfValue.reset();
this.channelFormGroup.controls.minConfValue.enable();
this.channelFormGroup.controls.minConfValue.setValidators([Validators.required]);
} else {
this.channelFormGroup.controls.selFeeRate.enable();
this.channelFormGroup.controls.minConfValue.setValue(null);
this.channelFormGroup.controls.minConfValue.disable();
this.channelFormGroup.controls.minConfValue.setValidators(null);
}
});
this.channelFormGroup.controls.selFeeRate.valueChanges.pipe(takeUntil(this.unSubs[1])).subscribe((feeRate) => {
this.channelFormGroup.controls.customFeeRate.setValue(null);
this.channelFormGroup.controls.customFeeRate.reset();
if (feeRate === 'customperkb' && !this.channelFormGroup.controls.flgMinConf.value) {
this.channelFormGroup.controls.customFeeRate.setValidators([Validators.required]);
} else {
this.channelFormGroup.controls.customFeeRate.setValidators(null);
}
});
this.actions.pipe(
takeUntil(this.unSubs[1]),
takeUntil(this.unSubs[2]),
filter((action) => action.type === CLActions.NEWLY_ADDED_PEER_CL || action.type === CLActions.FETCH_CHANNELS_CL || action.type === CLActions.UPDATE_API_CALL_STATUS_CL)).
subscribe((action: (CLActions.NewlyAddedPeer | CLActions.FetchChannels | CLActions.UpdateAPICallStatus)) => {
if (action.type === CLActions.NEWLY_ADDED_PEER_CL) {
@ -111,7 +129,8 @@ export class CLConnectPeerComponent implements OnInit, OnDestroy {
}
this.channelConnectionError = '';
this.store.dispatch(new CLActions.SaveNewChannel({
peerId: this.newlyAddedPeer.id, satoshis: this.channelFormGroup.controls.fundingAmount.value, announce: !this.channelFormGroup.controls.isPrivate.value, feeRate: this.channelFormGroup.controls.selFeeRate.value, minconf: this.channelFormGroup.controls.flgMinConf.value ? this.channelFormGroup.controls.minConfValue.value : null
peerId: this.newlyAddedPeer.id, satoshis: this.channelFormGroup.controls.fundingAmount.value, announce: !this.channelFormGroup.controls.isPrivate.value,
feeRate: (this.channelFormGroup.controls.selFeeRate.value === 'customperkb' && !this.channelFormGroup.controls.flgMinConf.value && this.channelFormGroup.controls.customFeeRate.value) ? ((this.channelFormGroup.controls.customFeeRate.value * 1000) + 'perkb') : this.channelFormGroup.controls.selFeeRate.value, minconf: this.channelFormGroup.controls.flgMinConf.value ? this.channelFormGroup.controls.minConfValue.value : null
}));
}

@ -285,7 +285,7 @@ export class UpdateChannels implements Action {
export class SaveNewChannel implements Action {
readonly type = SAVE_NEW_CHANNEL_CL;
constructor(public payload: {peerId: string, satoshis: string, feeRate: string, announce: boolean, minconf?: number, utxos?: string[]}) {}
constructor(public payload: {peerId: string, satoshis: string, announce: boolean, feeRate?: string, minconf?: number, utxos?: string[]}) {}
}

@ -44,7 +44,8 @@ export const FEE_LIMIT_TYPES = [
export const FEE_RATE_TYPES = [
{ feeRateId: 'urgent', feeRateType: 'Urgent' },
{ feeRateId: 'normal', feeRateType: 'Normal' },
{ feeRateId: 'slow', feeRateType: 'Slow' }
{ feeRateId: 'slow', feeRateType: 'Slow' },
{ feeRateId: 'customperkb', feeRateType: 'Custom (Sats/vB)' }
];

@ -560,6 +560,16 @@ mat-cell:last-of-type, mat-header-cell:last-of-type, mat-footer-cell:last-of-typ
}
}
.mr-6 {
margin-right: 6rem !important;
@include for_screensize(tab-port) {
margin-right: 3rem !important;
}
@include for_screensize(phone) {
margin-right: 2rem !important;
}
}
.mx-2 {
margin: 0 2rem !important;
@include for_screensize(tab-port) {

Loading…
Cancel
Save