send working with uris

This commit is contained in:
Matias Alejo Garcia 2014-12-08 23:45:34 -03:00
parent 80e22c59a0
commit 97c93e7909
3 changed files with 223 additions and 237 deletions

View File

@ -22,7 +22,6 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.alternativeName = w.settings.alternativeName; $scope.alternativeName = w.settings.alternativeName;
$scope.alternativeIsoCode = w.settings.alternativeIsoCode; $scope.alternativeIsoCode = w.settings.alternativeIsoCode;
$scope.isPayUri = false;
$scope.isRateAvailable = false; $scope.isRateAvailable = false;
$scope.rateService = rateService; $scope.rateService = rateService;
$scope.showScanner = false; $scope.showScanner = false;
@ -31,6 +30,7 @@ angular.module('copayApp.controllers').controller('SendController',
if ($rootScope.pendingPayment) { if ($rootScope.pendingPayment) {
$scope.setFromUri($rootScope.pendingPayment) $scope.setFromUri($rootScope.pendingPayment)
$rootScope.pendingPayment = null;
} }
$scope.setInputs(); $scope.setInputs();
@ -49,12 +49,12 @@ angular.module('copayApp.controllers').controller('SendController',
* *
*/ */
Object.defineProperty($scope, Object.defineProperty($scope,
"alternative", { "_alternative", {
get: function() { get: function() {
return this._alternative; return this.__alternative;
}, },
set: function(newValue) { set: function(newValue) {
this._alternative = newValue; this.__alternative = newValue;
if (typeof(newValue) === 'number' && $scope.isRateAvailable) { if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
this._amount = parseFloat( this._amount = parseFloat(
(rateService.fromFiat(newValue, $scope.alternativeIsoCode) * satToUnit).toFixed(w.settings.unitDecimals), 10); (rateService.fromFiat(newValue, $scope.alternativeIsoCode) * satToUnit).toFixed(w.settings.unitDecimals), 10);
@ -66,18 +66,18 @@ angular.module('copayApp.controllers').controller('SendController',
configurable: true configurable: true
}); });
Object.defineProperty($scope, Object.defineProperty($scope,
"amount", { "_amount", {
get: function() { get: function() {
return this._amount; return this.__amount;
}, },
set: function(newValue) { set: function(newValue) {
this._amount = newValue; this.__amount = newValue;
if (typeof(newValue) === 'number' && $scope.isRateAvailable) { if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
this._alternative = parseFloat( this.__alternative = parseFloat(
(rateService.toFiat(newValue * unitToSat, $scope.alternativeIsoCode)).toFixed(2), 10); (rateService.toFiat(newValue * unitToSat, $scope.alternativeIsoCode)).toFixed(2), 10);
} else { } else {
this._alternative = 0; this.__alternative = 0;
} }
}, },
enumerable: true, enumerable: true,
@ -85,12 +85,12 @@ angular.module('copayApp.controllers').controller('SendController',
}); });
Object.defineProperty($scope, Object.defineProperty($scope,
"address", { "_address", {
get: function() { get: function() {
return this._address; return this.__address;
}, },
set: function(newValue) { set: function(newValue) {
this._address = $scope.onAddressChange(newValue); this.__address = $scope.onAddressChange(newValue);
}, },
enumerable: true, enumerable: true,
configurable: true configurable: true
@ -109,8 +109,8 @@ angular.module('copayApp.controllers').controller('SendController',
}; };
$scope._showError = function(err) { $scope.setError = function(err) {
copay.logger.error(err); copay.logger.warn(err);
var msg = err.toString(); var msg = err.toString();
if (msg.match('BIG')) if (msg.match('BIG'))
@ -123,7 +123,10 @@ angular.module('copayApp.controllers').controller('SendController',
' could not be created: ' + msg; ' could not be created: ' + msg;
$scope.error = message; $scope.error = message;
$scope.loading = false;
$timeout(function(){
$scope.$digest();
},1);
}; };
$scope.submitForm = function(form) { $scope.submitForm = function(form) {
@ -134,35 +137,23 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.loading = true; $scope.loading = true;
var url = $scope._url;
var address = form.address.$modelValue; var address = form.address.$modelValue;
var amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0)); var amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
var commentText = form.comment.$modelValue; var comment = form.comment.$modelValue;
var payInfo;
if (address.indexOf('bitcoin:') === 0) {
payInfo = (new bitcore.BIP21(address)).data;
} else if (/^https?:\/\//.test(address)) {
payInfo = {
merchant: address
};
}
w.spend({ w.spend({
toAddress: address, toAddress: address,
amountSat: amount, amountSat: amount,
comment: commentText, comment: comment,
url: (payInfo && payInfo.merchant) ? payInfo.merchant : null, url: url,
}, function(err, txid, status) { }, function(err, txid, status) {
$scope.loading = false; $scope.loading = false;
// reset fields
$scope.address = $scope.amount = $scope.commentText = null;
form.address.$pristine = form.amount.$pristine = true;
$rootScope.pendingPayment = null;
$scope.isPayUri = null;
if (err) return $scope._showError(err);
$scope.notifyStatus(status); if (err)
return $scope.setError(err);
$scope.resetForm(status);
}); });
}; };
@ -303,7 +294,10 @@ angular.module('copayApp.controllers').controller('SendController',
} }
$scope.setTopAmount = function() { $scope.setTopAmount = function() {
$scope.amount = $rootScope.topAmount; var form = $scope.sendForm;
form.amount.$setViewValue(w.balanceInfo.topAmount);
form.amount.$render();
form.amount.$isValid = true;
}; };
$scope.notifyStatus = function(status) { $scope.notifyStatus = function(status) {
@ -316,7 +310,7 @@ angular.module('copayApp.controllers').controller('SendController',
else if (status == copay.Wallet.TX_SIGNED_AND_BROADCASTED) else if (status == copay.Wallet.TX_SIGNED_AND_BROADCASTED)
$scope.success = 'Transaction signed and broadcasted!'; $scope.success = 'Transaction signed and broadcasted!';
else else
$scope.error = 'Unknown error occured'; $scope.error = status;
$timeout(function() { $timeout(function() {
$scope.$digest(); $scope.$digest();
@ -330,7 +324,7 @@ angular.module('copayApp.controllers').controller('SendController',
$rootScope.txAlertCount = 0; $rootScope.txAlertCount = 0;
w.issueTx(ntxid, function(err, txid, status) { w.issueTx(ntxid, function(err, txid, status) {
$scope.loading = false; $scope.loading = false;
$scope.notifyStatus(status); $scope.resetForm(status);
if (cb) return cb(); if (cb) return cb();
}); });
}; };
@ -338,30 +332,31 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.setForm = function(to, amount, comment) { $scope.setForm = function(to, amount, comment) {
var form = $scope.sendForm; var form = $scope.sendForm;
form.address.$setViewValue(to); form.address.$setViewValue(to);
form.address.$render();
form.address.$isValid = true; form.address.$isValid = true;
form.address.$render();
$scope.lockAddress = true; $scope.lockAddress = true;
if (amount) { if (amount) {
form.amount.$setViewValue(amount); form.amount.$setViewValue(""+amount);
form.amount.$render();
form.amount.$isValid = true; form.amount.$isValid = true;
form.amount.$render();
$scope.lockAmount = true; $scope.lockAmount = true;
} }
if (comment) { if (comment) {
form.commentText.$setViewValue(comment); form.comment.$setViewValue(comment);
form.comment.$isValid = true;
form.comment.$render();
} }
}; };
$scope.cancelSend = function(error) { $scope.resetForm = function(status) {
var form = $scope.sendForm; var form = $scope.sendForm;
if (error) form.address.$pristine = form.amount.$pristine = true;
$scope.error = error;
$scope.fetchingURL = null; $scope.fetchingURL = null;
$scope.isPayUri = null; $scope.lockAddress = false;
$scope.lockAmount = false;
form.address.$setViewValue(''); form.address.$setViewValue('');
form.address.$render(); form.address.$render();
form.amount.$setViewValue(''); form.amount.$setViewValue('');
@ -369,6 +364,11 @@ angular.module('copayApp.controllers').controller('SendController',
form.comment.$setViewValue(''); form.comment.$setViewValue('');
form.comment.$render(); form.comment.$render();
form.$setPristine(); form.$setPristine();
$scope.notifyStatus(status);
$timeout(function(){
$rootScope.$digest();
},1);
}; };
@ -405,12 +405,12 @@ angular.module('copayApp.controllers').controller('SendController',
if (err) { if (err) {
if (err.match('TIMEOUT')) { if (err.match('TIMEOUT')) {
$scope.cancelSend('Payment server timed out'); $scope.resetForm('Payment server timed out');
} else { } else {
$scope.cancelSend(err.toString()); $scope.resetForm(err.toString());
} }
} else if (merchantData && available < +merchantData.total) { } else if (merchantData && available < +merchantData.total) {
$scope.cancelSend('Insufficient funds'); $scope.resetForm('Insufficient funds');
} else { } else {
$scope.setForm(merchantData.domain, merchantData.unitTotal) $scope.setForm(merchantData.domain, merchantData.unitTotal)
} }
@ -435,7 +435,7 @@ console.log('[send.js.430:parsed:]',addr,parsed.data); //TODO
return $scope.setFromPayPro(parsed.data.merchant); return $scope.setFromPayPro(parsed.data.merchant);
var amount = (parsed.data && parsed.data.amount) ? var amount = (parsed.data && parsed.data.amount) ?
parsed.data.amount * 100000000 * satToUnit : 0; (parsed.data.amount * 100000000).toFixed(0) * satToUnit : 0;
$scope.setForm(addr, amount, parsed.data.message, true); $scope.setForm(addr, amount, parsed.data.message, true);
return addr; return addr;
@ -527,7 +527,7 @@ console.log('[send.js.430:parsed:]',addr,parsed.data); //TODO
}); });
modalInstance.result.then(function(addr) { modalInstance.result.then(function(addr) {
$scope.address = addr; $scope._address = addr;
}); });
}; };

View File

@ -32,12 +32,6 @@ angular.module('copayApp.directives')
link: function(scope, elem, attrs, ctrl) { link: function(scope, elem, attrs, ctrl) {
var validator = function(value) { var validator = function(value) {
// If we're setting the domain, ignore the change.
if ($rootScope.merchant && $rootScope.merchant.domain && value === $rootScope.merchant.domain) {
ctrl.$setValidity('validAddress', true);
return value;
}
// Regular url // Regular url
if (/^https?:\/\//.test(value)) { if (/^https?:\/\//.test(value)) {
ctrl.$setValidity('validAddress', true); ctrl.$setValidity('validAddress', true);
@ -53,6 +47,11 @@ angular.module('copayApp.directives')
return value; return value;
} }
if (typeof value == 'undefined') {
ctrl.$pristine = true;
return;
}
// Regular Address // Regular Address
var a = new Address(value); var a = new Address(value);
ctrl.$setValidity('validAddress', a.isValid() && a.network().name === $rootScope.wallet.getNetworkName()); ctrl.$setValidity('validAddress', a.isValid() && a.network().name === $rootScope.wallet.getNetworkName());
@ -111,14 +110,11 @@ angular.module('copayApp.directives')
var str_value = ('' + value).substring(sep_index + 1); var str_value = ('' + value).substring(sep_index + 1);
if (sep_index > 0 && str_value.length > decimals) { if (sep_index > 0 && str_value.length > decimals) {
ctrl.$setValidity('validAmount', false); ctrl.$setValidity('validAmount', false);
scope.notValidAmount = true;
} else { } else {
ctrl.$setValidity('validAmount', true); ctrl.$setValidity('validAmount', true);
scope.notValidAmount = null;
} }
} else { } else {
ctrl.$setValidity('validAmount', false); ctrl.$setValidity('validAmount', false);
scope.notValidAmount = null;
} }
return value; return value;
} }

View File

@ -1,202 +1,192 @@
<div class="send" ng-controller="SendController" ng-init="init()"> <div class="send" ng-controller="SendController" ng-init="init()">
<div class="row"> <div class="row">
<div class="large-12 medium-12 small-12 columns"> <div class="large-12 medium-12 small-12 columns">
<h1 class="hide-for-large-up">{{$root.title}}</h1> <h1 class="hide-for-large-up">{{$root.title}}</h1>
</div>
</div> </div>
</div>
<div class="row"> <div class="row">
<div class="large-12 columns"> <div class="large-12 columns">
<form name="sendForm" ng-submit="submitForm(sendForm)" novalidate> <form name="sendForm" ng-submit="submitForm(sendForm)" novalidate>
<div class="panel"> <div class="panel">
<div class="box-notification" ng-show="error"> <div class="box-notification" ng-show="error">
<div class="box-icon error"> <div class="box-icon error">
<i class="fi-x size-24"></i> <i class="fi-x size-24"></i>
</div> </div>
<span class="text-warning size-14"> <span class="text-warning size-14">
{{error|translate}} {{error|translate}}
</span> </span>
</div> </div>
<div class="box-notification" ng-show="success"> <div class="box-notification" ng-show="success">
<div class="box-icon success"> <div class="box-icon success">
<i class="fi-check size-24"></i> <i class="fi-check size-24"></i>
</div> </div>
<span class="text-success size-14"> <span class="text-success size-14">
{{success|translate}} {{success|translate}}
</span> </span>
</div> </div>
<div class="row collapse"> <div ng-show="!_url">
<label for="address" class="left"> <div class="row collapse">
<span translate>To</span>
<i class="fi-info size-12" href="#" <label for="address" class="left">
data-options="disable_for_touch:true" <span translate>To (*)</span>
tooltip-popup-delay='500' </label>
tooltip="{{'Enter a valid Bitcoin address. Payment Protocol URLs are also supported'|translate}}"
tooltip-trigger="mouseenter" <span ng-hide="sendForm.address.$pristine">
tooltip-placement="right"></i> <span translate class="has-error right size-12" ng-show="sendForm.address.$invalid">
<small translate ng-hide="!sendForm.address.$pristine && address">required</small> <span class="icon-input">
</label> <i class="fi-x"></i>
<span translate class="has-error right size-12" ng-show="sendForm.address.$invalid && address">
<span class="icon-input"><i class="fi-x"></i></span>
Not valid
</span> </span>
<small class="icon-input right" ng-show="!sendForm.address.$invalid && address"><i class="fi-check"></i></small> Not valid
</div> </span>
<small class="icon-input right" ng-show="!sendForm.address.$invalid">
<i class="fi-check"></i>
</small>
</span>
</div>
<div class="input"> <div class="input">
<input type="text" id="address" name="address" ng-disabled="loading || lockAddress" <input type="text" id="address" name="address" ng-disabled="loading || lockAddress" placeholder="{{'Bitcoin address'|translate}}" ng-model="_address" valid-address required>
placeholder="{{'Bitcoin address'|translate}}" ng-model="address" valid-address required> <i class="fi-address-book"></i>
<i class="fi-address-book"></i> <div ng-hide="showScanner || disableScanner">
<div ng-hide="showScanner || disableScanner"> <a class="postfix button black" ng-click="openScanner()"><i class="fi-camera size-24"></i></a>
<a class="postfix button black" ng-click="openScanner()"><i class="fi-camera size-24"></i></a> </div>
<div ng-show="showScanner">
<a translate class="postfix button warning" ng-click="cancelScanner()"><i class="fi-x size-18"></i></a>
</div>
</div>
<div ng-show="_url">
<div class="row collapse">
{{_url}}
</div>
</div>
<div id="scanner" class="row" ng-if="showScanner" ng-include="'views/includes/scanner.html'">
</div>
</div>
<div class="row" ng-init="showAlternative = false">
<div class="large-12 medium-12 columns">
<div class="m5b right" ng-hide="sendForm.amount.$pristine">
<span translate class="has-error right size-12" ng-show="sendForm.amount.$invalid">
<span class="icon-input"><i class="fi-x"></i></span>
Not valid
</span>
<small class="icon-input right" ng-show="!sendForm.amount.$invalid">
<i class="fi-check"></i>
</small>
</div> </div>
<div ng-show="showScanner"> <div ng-show="!showAlternative">
<a translate class="postfix button warning" ng-click="cancelScanner()"><i class="fi-x size-18"></i></a> <div class="row collapse">
</div>
</div>
<div id="scanner" <label for="amount">
class="row" <span translate>Amount (*)</span>
ng-if="showScanner" </label>
ng-include="'views/includes/scanner.html'">
</div>
<div class="small-9 columns">
<div class="row" ng-init="showAlternative = false"> <div class="input">
<div class="large-12 medium-12 columns"> <input type="number" id="amount" ng-disabled="loading || lockAmount" name="amount" placeholder="{{'Amount'|translate}}" ng-minlength="0.00000001" ng-maxlength="10000000000" ng-model="_amount" valid-amount required autocomplete="off">
<div class="m5b right"> <i class="icon-bitcoin"></i>
<span translate class="has-error right size-12" ng-show="sendForm.amount.$invalid &&
!sendForm.amount.$pristine && (notValidAmount || amount)">
<span class="icon-input"><i class="fi-x"></i></span>
Not valid
</span>
<small class="icon-input right" ng-show="!sendForm.amount.$invalid &&
!sendForm.amount.$pristine"><i class="fi-check"></i></small>
</div>
<div ng-show="!showAlternative">
<div class="row collapse">
<label for="amount"><span translate>Amount
<small translate ng-hide="!sendForm.amount.$pristine && amount">required</small>
</label>
<div class="small-9 columns">
<div class="input">
<input type="number" id="amount"
ng-disabled="loading || lockAmount"
name="amount" placeholder="{{'Amount'|translate}}" ng-model="amount"
ng-minlength="0.00000001" ng-maxlength="10000000000" valid-amount required
autocomplete="off">
<i class="icon-bitcoin"></i>
</div>
<a class="small input-note" title="{{'Send all funds'|translate}}"
ng-show="topAmount && (!$root.merchant || +$root.merchant.total === 0)"
ng-click="setTopAmount(sendForm)">
<span translate>Use all funds</span> {{$root.wallet.settings.unitName}}
</a>
</div>
<div class="small-3 columns pointer" ng-click="showAlternative = true">
<span class="postfix">{{$root.wallet.settings.unitName}}</span>
</div> </div>
</div> </div>
</div> <div class="small-3 columns pointer" ng-click="showAlternative = true">
<div ng-show="showAlternative"> <span class="postfix">{{$root.wallet.settings.unitName}}</span>
<div class="row collapse">
<label for="alternative"><span translate>Amount in</span> {{ alternativeName }}
<small translate ng-hide="!sendForm.amount.$pristine && amount">required</small>
</label>
<div class="small-9 columns">
<div class="input">
<input type="number" id="alternative"
ng-disabled="loading || !isRateAvailable || lockAmount"
name="alternative" placeholder="{{'Amount'|translate}}" ng-model="alternative" requiredautocomplete="off">
<i class="icon-usd"></i>
</div>
</div>
<div class="small-3 columns pointer" ng-click="showAlternative = false">
<span class="postfix">{{alternativeIsoCode}}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div ng-show="showAlternative">
<div class="row collapse">
<div class="row"> <label for="alternative"><span translate>Amount in</span> {{ alternativeName }} (*)
<div class="large-12 columns" ng-show="fetchingURL"> </label>
<h3> <div class="small-9 columns">
<i class="fi-bitcoin-circle icon-rotate spinner"></i> <div class="input">
Fetching payment <input type="number" id="alternative" ng-disabled="loading || !isRateAvailable || lockAmount" name="alternative" placeholder="{{'Amount'|translate}}" ng-model="_alternative" requiredautocomplete="off">
</h3> <i class="icon-usd"></i>
<p> From {{fetchingURL}} </div>
</div> </div>
<div class="small-3 columns pointer" ng-click="showAlternative = false">
<div class="large-12 columns" ng-show="!!$root.merchant"> <span class="postfix">{{alternativeIsoCode}}</span>
<h3 >Payment Protocol Request</h3> </div>
<div class="send-note" ng-click="openPPModal(btx)">
<p>
<span ng-show="!!$root.merchant.pr.ca"><i class="fi-lock green"></i> {{$root.merchant.pr.ca}}</span>
<span ng-show="!$root.merchant.pr.ca"><i class="fi-unlock red"></i> Untrusted</span>
{{$root.merchant.pr.pd.memo || address}}
</p>
<p>
<i>{{amount}} {{$root.wallet.settings.unitName}}</i>
<span class="text-gray" ng-if="isRateAvailable">
{{ alternative }} {{ alternativeIsoCode }}
</span>
<p ng-show="!!$root.merchant">
Expires {{$root.merchant.expiration | amTimeAgo }}
[{{$root.merchant.domain}}]
</p>
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="large-12 columns">
<label for="comment"><span translate>Note</span>
<small translate ng-hide="!sendForm.comment.$pristine">optional</small>
<small translate class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
</label>
<div class="input">
<textarea id="comment" ng-disabled="loading"
name="comment" placeholder="{{($root.wallet.isShared() ? 'Leave a private message to your copayers' : 'Add a private comment to identify the transaction')}}" ng-model="commentText" ng-maxlength="100"></textarea>
<i class="icon-compose"></i>
</div>
</div>
</div>
<div class="row">
<div class="large-6 medium-6 small-6 columns text-left">
<a class="button tiny secondary m0" title="Address book" ng-hide="!!$root.merchant || lockAddress" ng-click="openAddressBook()">
<i class="fi-address-book"></i> Address book
</a>
<a ng-click="cancelSend(sendForm)" class="button warning m0" ng-show="!!$root.merchant || lockAddress">
Cancel
</a>
</div>
<div class="large-6 medium-6 small-6 columns text-right">
<button type="submit" class="button primary m0" ng-disabled="sendForm.$invalid || loading">
<i class="fi-bitcoin-circle icon-rotate spinner" ng-show="loading"></i>
Send
</button>
</div>
</div>
</div> </div>
</form>
</div> <div class="row">
</div><!-- end of row --> <div class="large-12 columns" ng-show="fetchingURL">
<h3>
<div class="row m20b" ng-show="$root.wallet.balanceInfo.alternativeConversionRate > 0"> <i class="fi-bitcoin-circle icon-rotate spinner"></i>
<div class="large-12 columns size-12"> Fetching payment
<i class="fi-bitcoin-circle"></i> </h3>
1 BTC = {{$root.wallet.balanceInfo.alternativeConversionRate}} {{alternativeIsoCode}} <p>From {{fetchingURL}}
</div> </div>
</div>
<div class="large-12 columns" ng-show="!!$root.merchant">
<h3>Payment Protocol Request</h3>
<div class="send-note" ng-click="openPPModal(btx)">
<p>
<span ng-show="!!$root.merchant.pr.ca"><i class="fi-lock green"></i> {{$root.merchant.pr.ca}}</span>
<span ng-show="!$root.merchant.pr.ca"><i class="fi-unlock red"></i> Untrusted</span>
{{$root.merchant.pr.pd.memo || address}} TODO ADDRESS
</p>
<p>TODO AMOUNT
<i>{{amount}} {{$root.wallet.settings.unitName}}</i>
<span class="text-gray" ng-if="isRateAvailable">
TODO ALT
{{ alternative }} {{ alternativeIsoCode }}
</span>
<p ng-show="!!$root.merchant">
Expires {{$root.merchant.expiration | amTimeAgo }} [{{$root.merchant.domain}}]
</p>
</div>
</div>
</div>
<div class="row">
<div class="large-12 columns">
<label for="comment"><span translate>Note</span>
<small translate ng-hide="!sendForm.comment.$pristine">optional</small>
<small translate class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
</label>
<div class="input">
<textarea id="comment" ng-disabled="loading" name="comment" placeholder="{{($root.wallet.isShared() ? 'Leave a private message to your copayers' : 'Add a private comment to identify the transaction')}}" ng-maxlength="100" ng-model="_comment"></textarea>
<i class="icon-compose"></i>
</div>
</div>
</div>
<div class="row">
<div class="large-6 medium-6 small-6 columns text-left">
<a class="button tiny secondary m0" title="Address book" ng-hide="!!$root.merchant || lockAddress" ng-click="openAddressBook()">
<i class="fi-address-book"></i> Address book
</a>
<a ng-click="resetForm()" class="button warning m0" ng-show="!!$root.merchant || lockAddress" ng-disabled="loading">
Cancel
</a>
</div>
<div class="large-6 medium-6 small-6 columns text-right">
<button type="submit" class="button primary m0" ng-disabled="sendForm.$invalid || loading">
<i class="fi-bitcoin-circle icon-rotate spinner" ng-show="loading"></i> Send
</button>
</div>
</div>
</div>
</form>
</div>
</div>
<!-- end of row -->
<div class="row m20b" ng-show="$root.wallet.balanceInfo.alternativeConversionRate > 0">
<div class="large-12 columns size-12">
<i class="fi-bitcoin-circle"></i> 1 BTC = {{$root.wallet.balanceInfo.alternativeConversionRate}} {{alternativeIsoCode}}
</div>
</div>
</div> </div>