Merge pull request #6432 from cmgustavo/ref/custom-fee-02
Ref/custom fee 02
This commit is contained in:
commit
5ca3dc3cd2
5 changed files with 253 additions and 28 deletions
|
|
@ -570,7 +570,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
|
||||
if (usingCustomFee) {
|
||||
scope.customFeePerKB = tx.feeRate;
|
||||
scope.feePerSatByte = (tx.feeRate / 1000).toFixed();
|
||||
scope.feePerSatByte = tx.feeRate / 1000;
|
||||
}
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', {
|
||||
|
|
|
|||
120
src/js/controllers/modals/feeLevels.js
Normal file
120
src/js/controllers/modals/feeLevels.js
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('feeLevelsController', function($scope, $timeout, $log, lodash, gettextCatalog, configService, feeService, ongoingProcess, popupService) {
|
||||
|
||||
var FEE_MULTIPLIER = 10;
|
||||
var FEE_MIN = 0;
|
||||
var FEE_MAX = 1000000;
|
||||
|
||||
var showErrorAndClose = function(title, msg) {
|
||||
title = title || gettextCatalog.getString('Error');
|
||||
$log.error(msg);
|
||||
popupService.showAlert(title, msg, function() {
|
||||
$scope.chooseFeeLevelModal.hide();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
var getMinRecommended = function() {
|
||||
var value = lodash.find($scope.feeLevels[$scope.network], {
|
||||
level: 'superEconomy'
|
||||
});
|
||||
return parseInt((value.feePerKB / 1000).toFixed());
|
||||
};
|
||||
|
||||
var getMaxRecommended = function() {
|
||||
var value = lodash.find($scope.feeLevels[$scope.network], {
|
||||
level: 'urgent'
|
||||
});
|
||||
return parseInt((value.feePerKB / 1000).toFixed());
|
||||
};
|
||||
|
||||
$scope.ok = function() {
|
||||
$scope.customFeePerKB = $scope.customFeePerKB ? ($scope.customSatPerByte.value * 1000).toFixed() : null;
|
||||
$scope.hideModal($scope.feeLevel, $scope.customFeePerKB);
|
||||
};
|
||||
|
||||
$scope.setFeesRecommended = function() {
|
||||
$scope.minFeeAllowed = FEE_MIN;
|
||||
$scope.maxFeeAllowed = FEE_MAX;
|
||||
$scope.maxFeeRecommended = getMaxRecommended() * FEE_MULTIPLIER;
|
||||
$scope.minFeeRecommended = getMinRecommended();
|
||||
};
|
||||
|
||||
$scope.checkFees = function(feePerSatByte) {
|
||||
var fee = Number(feePerSatByte);
|
||||
|
||||
if (fee <= $scope.minFeeAllowed) $scope.showError = true;
|
||||
else $scope.showError = false;
|
||||
|
||||
if (fee > $scope.minFeeAllowed && fee < $scope.minFeeRecommended) $scope.showMinWarning = true;
|
||||
else $scope.showMinWarning = false;
|
||||
|
||||
if (fee < $scope.maxFeeAllowed && fee > $scope.maxFeeRecommended) $scope.showMaxWarning = true;
|
||||
else $scope.showMaxWarning = false;
|
||||
};
|
||||
|
||||
$scope.updateFeeRate = function() {
|
||||
var value = lodash.find($scope.feeLevels[$scope.network], {
|
||||
level: $scope.feeLevel
|
||||
});
|
||||
|
||||
// If no custom fee
|
||||
if (value) {
|
||||
$scope.customFeePerKB = null;
|
||||
$scope.feePerSatByte = (value.feePerKB / 1000).toFixed();
|
||||
$scope.avgConfirmationTime = value.nbBlocks * 10;
|
||||
} else {
|
||||
$scope.avgConfirmationTime = null;
|
||||
$scope.customSatPerByte = { value: Number($scope.feePerSatByte) };
|
||||
$scope.customFeePerKB = ($scope.feePerSatByte * 1000).toFixed();
|
||||
}
|
||||
|
||||
// Warnings
|
||||
$scope.setFeesRecommended();
|
||||
$scope.checkFees($scope.feePerSatByte);
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$watch(
|
||||
"selectedFee.value",
|
||||
function ( newValue, oldValue ) {
|
||||
if (newValue != oldValue) {
|
||||
$log.debug('New fee level: ' + newValue);
|
||||
$scope.feeLevel = $scope.selectedFee.value;
|
||||
$scope.updateFeeRate();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// From parent controller
|
||||
// $scope.network
|
||||
// $scope.feeLevel
|
||||
//
|
||||
// IF usingCustomFee
|
||||
// $scope.customFeePerKB
|
||||
// $scope.feePerSatByte
|
||||
|
||||
if (lodash.isEmpty($scope.feeLevel)) showErrorAndClose(null, gettextCatalog.getString('Fee level is not defined') );
|
||||
$scope.selectedFee = { value: $scope.feeLevel };
|
||||
|
||||
$scope.feeOpts = feeService.feeOpts;
|
||||
$scope.loadingFee = true;
|
||||
feeService.getFeeLevels(function(err, levels) {
|
||||
$scope.loadingFee = false;
|
||||
if (err || lodash.isEmpty(levels)) {
|
||||
showErrorAndClose(null, err);
|
||||
return;
|
||||
}
|
||||
if (lodash.isEmpty(levels)) {
|
||||
showErrorAndClose(null, gettextCatalog.getString('Could not get fee levels'));
|
||||
return;
|
||||
}
|
||||
$scope.feeLevels = levels;
|
||||
$scope.updateFeeRate();
|
||||
});
|
||||
|
||||
});
|
||||
63
src/sass/views/includes/modals/choose-fee-level.scss
Normal file
63
src/sass/views/includes/modals/choose-fee-level.scss
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
#choose-fee-level {
|
||||
@extend .deflash-blue;
|
||||
.selected-fee-level {
|
||||
text-align: center;
|
||||
background: #f9f9f9;
|
||||
font-size: 11px;
|
||||
height: 120px;
|
||||
padding-top: 25px;
|
||||
.row {
|
||||
padding: 0;
|
||||
}
|
||||
.separator {
|
||||
border-left: 1px solid #d9d9df;
|
||||
height: 75%;
|
||||
}
|
||||
.value {
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.rate .list {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.item-input {
|
||||
input[type="number"] {
|
||||
text-align: right;
|
||||
padding-right: 90px;
|
||||
}
|
||||
.unit {
|
||||
color: #9c9c9c;
|
||||
background: #f2f2f2;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 15px 10px 12px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.warning-fee {
|
||||
padding: 5px 10px;
|
||||
color: $v-warning-color;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.error-fee {
|
||||
padding: 5px 10px;
|
||||
color: $v-error-color;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.box-notification {
|
||||
margin: 0;
|
||||
padding: 1px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
@import "includes/walletActivity";
|
||||
@import "includes/wallets";
|
||||
@import "includes/modals/modals";
|
||||
@import "includes/modals/choose-fee-level";
|
||||
@import "includes/clickToAccept";
|
||||
@import "includes/incomingDataMenu";
|
||||
@import "includes/slideToAccept";
|
||||
|
|
|
|||
|
|
@ -1,41 +1,82 @@
|
|||
<ion-modal-view id="settings-fee" class="settings" ng-controller="preferencesFeeController">
|
||||
<ion-modal-view id="choose-fee-level" ng-controller="feeLevelsController">
|
||||
<ion-header-bar align-title="center" class="bar-royal">
|
||||
<div class="title">
|
||||
{{'Bitcoin Network Fee Policy'|translate}}
|
||||
</div>
|
||||
<button ng-disabled="invalidCustomFeeEntered" class="button button-clear" ng-click="chooseNewFee()">
|
||||
<button
|
||||
ng-disabled="customFeePerKB && !customSatPerByte.value"
|
||||
class="button button-clear" ng-click="ok()" translate>
|
||||
OK
|
||||
</button>
|
||||
</ion-header-bar>
|
||||
<ion-content ng-init="init()">
|
||||
<div class="settings-explanation">
|
||||
<div class="estimates">
|
||||
<div>
|
||||
<span translate>Average confirmation time</span>:
|
||||
<span class="fee-minutes" ng-if="avgConfirmationTime && currentFeeLevel != 'custom'">{{avgConfirmationTime | amDurationFormat: 'minute'}}</span>
|
||||
<span class="fee-minutes" ng-if="currentFeeLevel == 'custom' && !invalidCustomFeeEntered && !loadingFee" translate>Could not be estimated</span>
|
||||
<span ng-if="loadingFee || invalidCustomFeeEntered">...</span>
|
||||
<ion-content>
|
||||
<div class="box-notification warning" ng-if="network!='livenet'">
|
||||
Testnet
|
||||
</div>
|
||||
<div class="row selected-fee-level" ng-show="feeLevel">
|
||||
<div class="col time" ng-if="!customFeePerKB">
|
||||
<div class="value">
|
||||
<span ng-if="avgConfirmationTime">
|
||||
{{avgConfirmationTime | amDurationFormat: 'minute'}}
|
||||
</span>
|
||||
<span ng-if="loadingFee">...</span>
|
||||
</div>
|
||||
<div>
|
||||
<span translate>Current fee rate for this policy</span>:
|
||||
<span class="fee-rate" ng-if="feePerSatByte && !invalidCustomFeeEntered && !loadingFee">{{feePerSatByte}} satoshis/byte</span>
|
||||
<span ng-if="loadingFee || invalidCustomFeeEntered">...</span>
|
||||
<span translate>Average confirmation time</span>
|
||||
</div>
|
||||
<div class="col rate" ng-class="{'separator': !customFeePerKB}">
|
||||
<div ng-if="!customFeePerKB">
|
||||
<div class="value">
|
||||
<span ng-if="feePerSatByte && !loadingFee">
|
||||
{{feePerSatByte}} sat/byte
|
||||
</span>
|
||||
<span ng-if="loadingFee">...</span>
|
||||
</div>
|
||||
<span translate>Current fee rate for this policy</span>
|
||||
</div>
|
||||
<div ng-if="customFeePerKB">
|
||||
<div class="list">
|
||||
<label class="item item-input">
|
||||
<input
|
||||
type="number"
|
||||
placeholder="{{'Enter custom fee'|translate}}"
|
||||
ng-min="minFeeAllowed"
|
||||
ng-max="maxFeeAllowed"
|
||||
min="minFeeRecommended"
|
||||
max="maxFeeRecommended"
|
||||
ng-change="checkFees(customSatPerByte.value)"
|
||||
ng-model="customSatPerByte.value"
|
||||
ng-required="customFeePerKB">
|
||||
<span class="unit">sat/byte</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="error-fee" ng-if="showError">
|
||||
<i class="icon ion-close-circled"></i>
|
||||
<span translate>
|
||||
Transactions without fee are not supported.
|
||||
</span>
|
||||
</div>
|
||||
<div class="warning-fee" ng-if="showMinWarning || showMaxWarning">
|
||||
<i class="icon ion-alert-circled"></i>
|
||||
<span ng-if="showMinWarning" translate>
|
||||
Your fee is lower than recommended.
|
||||
</span>
|
||||
<span ng-if="showMaxWarning" translate>
|
||||
You should not set a fee higher than {{maxFeeRecommended}} satoshis/byte.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span ng-if="network!='livenet'">[{{network}}]</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fee-policies">
|
||||
<ion-radio ng-repeat="(fee, level) in feeOpts" ng-value="fee" ng-model="currentFeeLevel" ng-click="save(fee)">
|
||||
{{level|translate}}
|
||||
</ion-radio>
|
||||
</div>
|
||||
<div class="comment" ng-if="showMinWarning">
|
||||
<i class="icon"><img src="img/icon-warning.png"></i>
|
||||
<span class="text" translate>Your fee is lower than recommended super economy fee ({{getMinimumRecommeded()}} satoshis/byte). The transaction may never get confirmed.</span>
|
||||
</div>
|
||||
<div class="comment" ng-if="showMaxWarning">
|
||||
<i class="icon"><img src="img/icon-warning.png"></i>
|
||||
<span class="text" translate>You could not set a fee higher than 1000 satoshis/byte.</span>
|
||||
|
||||
<div class="list" ng-show="feeLevel">
|
||||
<label class="item item-input item-select">
|
||||
<div class="input-label" translate>
|
||||
Fee level
|
||||
</div>
|
||||
<select ng-options="fee as level for (fee,level) in feeOpts" ng-model="selectedFee.value">
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</ion-content>
|
||||
</ion-modal-view>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue