Merge pull request #1228 from eordano/feature/fiat
Allow user to select an alternative currency and specify amount of money in a fiat currency
This commit is contained in:
commit
e73a3f8160
13 changed files with 288 additions and 24 deletions
|
|
@ -7,6 +7,8 @@ var defaultConfig = {
|
||||||
// DEFAULT unit: Bit
|
// DEFAULT unit: Bit
|
||||||
unitName: 'bits',
|
unitName: 'bits',
|
||||||
unitToSatoshi: 100,
|
unitToSatoshi: 100,
|
||||||
|
alternativeName: 'US Dollar',
|
||||||
|
alternativeIsoCode: 'USD',
|
||||||
|
|
||||||
// wallet limits
|
// wallet limits
|
||||||
limits: {
|
limits: {
|
||||||
|
|
@ -54,6 +56,11 @@ var defaultConfig = {
|
||||||
storageSalt: 'mjuBtGybi/4=',
|
storageSalt: 'mjuBtGybi/4=',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rate: {
|
||||||
|
url: 'https://bitpay.com/api/rates',
|
||||||
|
updateFrequencySeconds: 60 * 60
|
||||||
|
},
|
||||||
|
|
||||||
disableVideo: true,
|
disableVideo: true,
|
||||||
verbose: 1,
|
verbose: 1,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,67 @@
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('SendController',
|
angular.module('copayApp.controllers').controller('SendController',
|
||||||
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils) {
|
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) {
|
||||||
$scope.title = 'Send';
|
$scope.title = 'Send';
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / config.unitToSatoshi;
|
||||||
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
||||||
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
||||||
|
$scope.unitToSatoshi = config.unitToSatoshi;
|
||||||
$scope.minAmount = config.limits.minAmountSatoshi * satToUnit;
|
$scope.minAmount = config.limits.minAmountSatoshi * satToUnit;
|
||||||
|
|
||||||
|
$scope.alternativeName = config.alternativeName;
|
||||||
|
$scope.alternativeIsoCode = config.alternativeIsoCode;
|
||||||
|
|
||||||
|
$scope.isRateAvailable = false;
|
||||||
|
$scope.rateService = rateService;
|
||||||
|
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
$scope.isRateAvailable = true;
|
||||||
|
$scope.$digest();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setting the two related amounts as properties prevents an infinite
|
||||||
|
* recursion for watches while preserving the original angular updates
|
||||||
|
*/
|
||||||
|
Object.defineProperty($scope,
|
||||||
|
"alternative", {
|
||||||
|
get: function () {
|
||||||
|
return this._alternative;
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
this._alternative = newValue;
|
||||||
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||||
|
this._amount = Number.parseFloat(
|
||||||
|
(rateService.fromFiat(newValue, config.alternativeIsoCode) * satToUnit
|
||||||
|
).toFixed(config.unitDecimals), 10);
|
||||||
|
} else {
|
||||||
|
this._amount = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
Object.defineProperty($scope,
|
||||||
|
"amount", {
|
||||||
|
get: function () {
|
||||||
|
return this._amount;
|
||||||
|
},
|
||||||
|
set: function (newValue) {
|
||||||
|
this._amount = newValue;
|
||||||
|
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||||
|
this._alternative = Number.parseFloat(
|
||||||
|
(rateService.toFiat(newValue * config.unitToSatoshi, config.alternativeIsoCode)
|
||||||
|
).toFixed(2), 10);
|
||||||
|
} else {
|
||||||
|
this._alternative = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
$scope.loadTxs = function() {
|
$scope.loadTxs = function() {
|
||||||
var opts = {
|
var opts = {
|
||||||
pending: true,
|
pending: true,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils) {
|
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils, rateService) {
|
||||||
|
|
||||||
controllerUtils.redirIfLogged();
|
controllerUtils.redirIfLogged();
|
||||||
$scope.title = 'Settings';
|
$scope.title = 'Settings';
|
||||||
|
|
@ -14,21 +14,41 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
||||||
$scope.unitOpts = [{
|
$scope.unitOpts = [{
|
||||||
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||||
shortName: 'SAT',
|
shortName: 'SAT',
|
||||||
value: 1
|
value: 1,
|
||||||
|
decimals: 0
|
||||||
}, {
|
}, {
|
||||||
name: 'bits (1,000,000 bits = 1BTC)',
|
name: 'bits (1,000,000 bits = 1BTC)',
|
||||||
shortName: 'bits',
|
shortName: 'bits',
|
||||||
value: 100
|
value: 100,
|
||||||
|
decimals: 2
|
||||||
}, {
|
}, {
|
||||||
name: 'mBTC (1,000 mBTC = 1BTC)',
|
name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||||
shortName: 'mBTC',
|
shortName: 'mBTC',
|
||||||
value: 100000
|
value: 100000,
|
||||||
|
decimals: 5
|
||||||
}, {
|
}, {
|
||||||
name: 'BTC',
|
name: 'BTC',
|
||||||
shortName: 'BTC',
|
shortName: 'BTC',
|
||||||
value: 100000000
|
value: 100000000,
|
||||||
|
decimals: 8
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
$scope.selectedAlternative = {
|
||||||
|
name: config.alternativeName,
|
||||||
|
isoCode: config.alternativeIsoCode
|
||||||
|
};
|
||||||
|
$scope.alternativeOpts = rateService.isAvailable ?
|
||||||
|
rateService.listAlternatives() : [$scope.selectedAlternative];
|
||||||
|
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
$scope.alternativeOpts = rateService.listAlternatives();
|
||||||
|
for (var ii in $scope.alternativeOpts) {
|
||||||
|
if (config.alternativeIsoCode === $scope.alternativeOpts[ii].isoCode) {
|
||||||
|
$scope.selectedAlternative = $scope.alternativeOpts[ii];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for (var ii in $scope.unitOpts) {
|
for (var ii in $scope.unitOpts) {
|
||||||
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
||||||
$scope.selectedUnit = $scope.unitOpts[ii];
|
$scope.selectedUnit = $scope.unitOpts[ii];
|
||||||
|
|
@ -68,7 +88,11 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
||||||
disableVideo: $scope.disableVideo,
|
disableVideo: $scope.disableVideo,
|
||||||
unitName: $scope.selectedUnit.shortName,
|
unitName: $scope.selectedUnit.shortName,
|
||||||
unitToSatoshi: $scope.selectedUnit.value,
|
unitToSatoshi: $scope.selectedUnit.value,
|
||||||
version: copay.version,
|
unitDecimals: $scope.selectedUnit.decimals,
|
||||||
|
alternativeName: $scope.selectedAlternative.name,
|
||||||
|
alternativeIsoCode: $scope.selectedAlternative.isoCode,
|
||||||
|
|
||||||
|
version: copay.version
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Go home reloading the application
|
// Go home reloading the application
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
|
||||||
angular.module('copayApp.services')
|
angular.module('copayApp.services')
|
||||||
.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, video, uriHandler) {
|
.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, video, uriHandler, rateService) {
|
||||||
var root = {};
|
var root = {};
|
||||||
root.getVideoMutedStatus = function(copayer) {
|
root.getVideoMutedStatus = function(copayer) {
|
||||||
if (!$rootScope.videoInfo) return;
|
if (!$rootScope.videoInfo) return;
|
||||||
|
|
@ -217,7 +217,15 @@ angular.module('copayApp.services')
|
||||||
$rootScope.balanceByAddr = balanceByAddr;
|
$rootScope.balanceByAddr = balanceByAddr;
|
||||||
root.updateAddressList();
|
root.updateAddressList();
|
||||||
$rootScope.updatingBalance = false;
|
$rootScope.updatingBalance = false;
|
||||||
return cb ? cb() : null;
|
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
$rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, config.alternativeIsoCode);
|
||||||
|
$rootScope.alternativeIsoCode = config.alternativeIsoCode;
|
||||||
|
$rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, config.alternativeIsoCode);
|
||||||
|
|
||||||
|
|
||||||
|
return cb ? cb() : null;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
83
js/services/rate.js
Normal file
83
js/services/rate.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var RateService = function(request) {
|
||||||
|
this.isAvailable = false;
|
||||||
|
this.UNAVAILABLE_ERROR = 'Service is not available - check for service.isAvailable or use service.whenAvailable';
|
||||||
|
this.SAT_TO_BTC = 1 / 1e8;
|
||||||
|
var MINS_IN_HOUR = 60;
|
||||||
|
var MILLIS_IN_SECOND = 1000;
|
||||||
|
var rateServiceConfig = config.rate;
|
||||||
|
var updateFrequencySeconds = rateServiceConfig.updateFrequencySeconds || 60 * MINS_IN_HOUR;
|
||||||
|
var rateServiceUrl = rateServiceConfig.url || 'https://bitpay.com/api/rates';
|
||||||
|
this.queued = [];
|
||||||
|
this.alternatives = [];
|
||||||
|
var that = this;
|
||||||
|
var backoffSeconds = 5;
|
||||||
|
var retrieve = function() {
|
||||||
|
request.get({
|
||||||
|
url: rateServiceUrl,
|
||||||
|
json: true
|
||||||
|
}, function(err, response, listOfCurrencies) {
|
||||||
|
if (err) {
|
||||||
|
backoffSeconds *= 1.5;
|
||||||
|
setTimeout(retrieve, backoffSeconds * MILLIS_IN_SECOND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var rates = {};
|
||||||
|
listOfCurrencies.forEach(function(element) {
|
||||||
|
rates[element.code] = element.rate;
|
||||||
|
that.alternatives.push({
|
||||||
|
name: element.name,
|
||||||
|
isoCode: element.code,
|
||||||
|
rate: element.rate
|
||||||
|
});
|
||||||
|
});
|
||||||
|
that.isAvailable = true;
|
||||||
|
that.rates = rates;
|
||||||
|
that.queued.forEach(function(callback) {
|
||||||
|
setTimeout(callback, 1);
|
||||||
|
});
|
||||||
|
setTimeout(retrieve, updateFrequencySeconds * MILLIS_IN_SECOND);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
retrieve();
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.whenAvailable = function(callback) {
|
||||||
|
if (this.isAvailable) {
|
||||||
|
setTimeout(callback, 1);
|
||||||
|
} else {
|
||||||
|
this.queued.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.toFiat = function(satoshis, code) {
|
||||||
|
if (!this.isAvailable) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
return satoshis * this.SAT_TO_BTC * this.rates[code];
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.fromFiat = function(amount, code) {
|
||||||
|
if (!this.isAvailable) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
return amount / this.rates[code] / this.SAT_TO_BTC;
|
||||||
|
};
|
||||||
|
|
||||||
|
RateService.prototype.listAlternatives = function() {
|
||||||
|
if (!this.isAvailable) {
|
||||||
|
throw new Error(this.UNAVAILABLE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
var alts = [];
|
||||||
|
this.alternatives.forEach(function(element) {
|
||||||
|
alts.push({
|
||||||
|
name: element.name,
|
||||||
|
isoCode: element.isoCode
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return alts;
|
||||||
|
};
|
||||||
|
|
||||||
|
angular.module('copayApp.services').service('rateService', RateService);
|
||||||
6
js/services/request.js
Normal file
6
js/services/request.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('copayApp.services').factory('request', function() {
|
||||||
|
return require('request');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -72,7 +72,9 @@
|
||||||
"travis-cov": "0.2.5",
|
"travis-cov": "0.2.5",
|
||||||
"uglifyify": "1.2.3",
|
"uglifyify": "1.2.3",
|
||||||
"crypto-js": "3.1.2",
|
"crypto-js": "3.1.2",
|
||||||
"shelljs": "0.3.0"
|
"shelljs":"0.3.0",
|
||||||
|
"browser-request": "0.3.2",
|
||||||
|
"request": "2.40.0"
|
||||||
},
|
},
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"homepage": "https://github.com/bitpay/copay",
|
"homepage": "https://github.com/bitpay/copay",
|
||||||
|
|
|
||||||
0
test/run.sh
Normal file → Executable file
0
test/run.sh
Normal file → Executable file
|
|
@ -29,7 +29,9 @@ describe("Unit: Controllers", function() {
|
||||||
totalCopayers: 5,
|
totalCopayers: 5,
|
||||||
spendUnconfirmed: 1,
|
spendUnconfirmed: 1,
|
||||||
reconnectDelay: 100,
|
reconnectDelay: 100,
|
||||||
networkName: 'testnet'
|
networkName: 'testnet',
|
||||||
|
alternativeName: 'lol currency',
|
||||||
|
alternativeIsoCode: 'LOL'
|
||||||
};
|
};
|
||||||
|
|
||||||
it('Copay config should be binded', function() {
|
it('Copay config should be binded', function() {
|
||||||
|
|
@ -124,11 +126,21 @@ describe("Unit: Controllers", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Send Controller', function() {
|
describe('Send Controller', function() {
|
||||||
var scope, form, sendForm;
|
var scope, form, sendForm, sendCtrl;
|
||||||
beforeEach(angular.mock.module('copayApp'));
|
beforeEach(angular.mock.module('copayApp'));
|
||||||
beforeEach(angular.mock.inject(function($compile, $rootScope, $controller) {
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('request', {
|
||||||
|
'get': function(_, cb) {
|
||||||
|
cb(null, null, [{name: 'lol currency', code: 'LOL', rate: 2}]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
beforeEach(angular.mock.inject(function($compile, $rootScope, $controller, rateService) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
|
scope.rateService = rateService;
|
||||||
$rootScope.wallet = new FakeWallet(walletConfig);
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
|
config.alternativeName = 'lol currency';
|
||||||
|
config.alternativeIsoCode = 'LOL';
|
||||||
var element = angular.element(
|
var element = angular.element(
|
||||||
'<form name="form">' +
|
'<form name="form">' +
|
||||||
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
||||||
|
|
@ -147,11 +159,12 @@ describe("Unit: Controllers", function() {
|
||||||
'<form name="form2">' +
|
'<form name="form2">' +
|
||||||
'<input type="text" id="address" name="address" ng-model="address" valid-address required>' +
|
'<input type="text" id="address" name="address" ng-model="address" valid-address required>' +
|
||||||
'<input type="number" id="amount" name="amount" ng-model="amount" min="1" max="10000000000" required>' +
|
'<input type="number" id="amount" name="amount" ng-model="amount" min="1" max="10000000000" required>' +
|
||||||
|
'<input type="number" id="alternative" name="alternative" ng-model="alternative">' +
|
||||||
'<textarea id="comment" name="comment" ng-model="commentText" ng-maxlength="100"></textarea>' +
|
'<textarea id="comment" name="comment" ng-model="commentText" ng-maxlength="100"></textarea>' +
|
||||||
'</form>'
|
'</form>'
|
||||||
);
|
);
|
||||||
$compile(element2)(scope);
|
$compile(element2)(scope);
|
||||||
$controller('SendController', {
|
sendCtrl = $controller('SendController', {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$modal: {},
|
$modal: {},
|
||||||
});
|
});
|
||||||
|
|
@ -241,8 +254,22 @@ describe("Unit: Controllers", function() {
|
||||||
config.unitToSatoshi = old;
|
config.unitToSatoshi = old;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should convert bits amount to fiat', function(done) {
|
||||||
|
scope.rateService.whenAvailable(function() {
|
||||||
|
sendForm.amount.$setViewValue(1e6);
|
||||||
|
scope.$digest();
|
||||||
|
expect(scope.alternative).to.equal(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should convert fiat to bits amount', function(done) {
|
||||||
|
scope.rateService.whenAvailable(function() {
|
||||||
|
sendForm.alternative.$setViewValue(2);
|
||||||
|
scope.$digest();
|
||||||
|
expect(scope.amount).to.equal(1e6);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should create and send a transaction proposal', function() {
|
it('should create and send a transaction proposal', function() {
|
||||||
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,6 @@ describe("Unit: controllerUtils", function() {
|
||||||
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
|
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Unit: Notification Service", function() {
|
describe("Unit: Notification Service", function() {
|
||||||
|
|
@ -135,3 +133,36 @@ describe("Unit: uriHandler service", function() {
|
||||||
}).should.not.throw();
|
}).should.not.throw();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Unit: Rate Service', function() {
|
||||||
|
beforeEach(angular.mock.module('copayApp.services'));
|
||||||
|
it('should be injected correctly', inject(function(rateService) {
|
||||||
|
should.exist(rateService);
|
||||||
|
}));
|
||||||
|
it('should be possible to ask if it is available',
|
||||||
|
inject(function(rateService) {
|
||||||
|
should.exist(rateService.isAvailable);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
beforeEach(module(function($provide) {
|
||||||
|
$provide.value('request', {
|
||||||
|
'get': function(_, cb) {
|
||||||
|
cb(null, null, [{name: 'lol currency', code: 'LOL', rate: 2}]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
it('should be possible to ask for conversion from fiat',
|
||||||
|
inject(function(rateService) {
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
(1).should.equal(rateService.fromFiat(2, 'LOL'));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
it('should be possible to ask for conversion to fiat',
|
||||||
|
inject(function(rateService) {
|
||||||
|
rateService.whenAvailable(function() {
|
||||||
|
(2).should.equal(rateService.toFiat(1e8, 'LOL'));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,9 @@
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{totalBalanceBTC |noFractionNumber:8}} BTC"
|
tooltip="{{totalBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{totalBalance || 0
|
tooltip-placement="bottom">{{totalBalance || 0 |noFractionNumber}} {{$root.unitName}}
|
||||||
|noFractionNumber}} {{$root.unitName}}
|
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
Locked
|
Locked
|
||||||
|
|
@ -38,7 +37,7 @@
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{lockedBalanceBTC |noFractionNumber:8}} BTC"
|
tooltip="{{lockedBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
||||||
</span> <i class="fi-info medium" tooltip="Balance locked in pending transaction proposals" tooltip-placement="bottom"></i>
|
</span> <i class="fi-info medium" tooltip="Balance locked in pending transaction proposals" tooltip-placement="bottom"></i>
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="large-6 medium-6 columns">
|
||||||
|
<div class="row collapse">
|
||||||
|
<label for="alternative">Amount in {{ alternativeName }} </label>
|
||||||
|
<div class="small-9 columns">
|
||||||
|
<input type="number" id="alternative_amount"
|
||||||
|
ng-disabled="loading || !isRateAvailable "
|
||||||
|
name="alternative" placeholder="Amount" ng-model="alternative"
|
||||||
|
enough-amount required
|
||||||
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="small-3 columns">
|
||||||
|
<span class="postfix">{{alternativeIsoCode}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-show="wallet.isShared()">
|
<div class="row" ng-show="wallet.isShared()">
|
||||||
|
|
@ -110,7 +126,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="large-6 columns show-for-large-up">
|
<div class="large-6 columns show-for-large-up" ng-show="!!$root.merchant">
|
||||||
<div class="send-note">
|
<div class="send-note">
|
||||||
<h6>Send to</h6>
|
<h6>Send to</h6>
|
||||||
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
||||||
|
|
@ -119,8 +135,11 @@
|
||||||
<h6>Total amount for this transaction:</h6>
|
<h6>Total amount for this transaction:</h6>
|
||||||
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
||||||
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
||||||
|
<small ng-if="isRateAvailable">
|
||||||
|
{{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }}
|
||||||
|
<br>
|
||||||
|
</small>
|
||||||
<small>
|
<small>
|
||||||
{{ ((amount + defaultFee) * unitToBtc)|noFractionNumber:8}} BTC <br/>
|
|
||||||
Including fee of {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
Including fee of {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@
|
||||||
<select class="form-control" ng-model="selectedUnit" ng-options="o.name for o in unitOpts" required>
|
<select class="form-control" ng-model="selectedUnit" ng-options="o.name for o in unitOpts" required>
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Alternative Currency</legend>
|
||||||
|
<select class="form-control" ng-model="selectedAlternative" ng-options="alternative.name for alternative in alternativeOpts" required>
|
||||||
|
</select>
|
||||||
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Videoconferencing</legend>
|
<legend>Videoconferencing</legend>
|
||||||
<input id="disableVideo-opt" type="checkbox" ng-model="disableVideo" class="form-control">
|
<input id="disableVideo-opt" type="checkbox" ng-model="disableVideo" class="form-control">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue