copay/old/walletHome.js

945 lines
27 KiB
JavaScript
Raw Normal View History

2015-03-06 07:00:10 -08:00
'use strict';
2016-08-08 08:14:04 -07:00
angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, notification, txStatus, profileService, lodash, configService, rateService, storageService, bitcore, gettext, gettextCatalog, platformInfo, addressService, ledger, bwcError, confirmDialog, txFormatService, addressbookService, go, feeService, walletService, fingerprintService, nodeWebkit, ongoingProcess) {
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
var isAndroid = platformInfo.isAndroid;
2016-05-17 07:09:22 -07:00
var isChromeApp = platformInfo.isChromeApp;
2015-03-06 07:00:10 -08:00
2015-04-23 08:27:43 -07:00
var self = this;
2016-02-03 08:35:52 -08:00
$rootScope.shouldHideMenuBar = false;
2015-04-23 08:27:43 -07:00
$rootScope.wpInputFocused = false;
2015-10-07 12:17:19 -07:00
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
var ret = {};
// INIT. Global value
ret.unitToSatoshi = walletSettings.unitToSatoshi;
ret.satToUnit = 1 / ret.unitToSatoshi;
ret.unitName = walletSettings.unitName;
ret.alternativeIsoCode = walletSettings.alternativeIsoCode;
ret.alternativeName = walletSettings.alternativeName;
ret.alternativeAmount = 0;
ret.unitDecimals = walletSettings.unitDecimals;
ret.isCordova = isCordova;
ret.addresses = [];
ret.isMobile = platformInfo.isMobile;
ret.isWindowsPhoneApp = platformInfo.isWP;
2016-03-30 08:52:19 -07:00
ret.countDown = null;
ret.sendMaxInfo = {};
2016-07-18 07:28:03 -07:00
ret.showAlternative = false;
ret.fromInputAmount = null;
var vanillaScope = ret;
2015-04-23 08:27:43 -07:00
var disableScannerListener = $rootScope.$on('dataScanned', function(event, data) {
if (!data) return;
2015-04-23 08:27:43 -07:00
self.setForm(data);
2015-04-28 14:11:06 -07:00
$rootScope.$emit('Local/SetTab', 'send');
var form = $scope.sendForm;
if (form.address.$invalid && !ongoingProcess.get('fetchingPayPro')) {
2015-07-15 12:15:05 -07:00
self.resetForm();
self.error = gettext('Could not recognize a valid Bitcoin QR Code');
}
2015-04-23 08:27:43 -07:00
});
var disablePaymentUriListener = $rootScope.$on('paymentUri', function(event, uri) {
2015-12-09 11:55:56 -08:00
$rootScope.$emit('Local/SetTab', 'send');
2015-04-23 08:27:43 -07:00
$timeout(function() {
self.setForm(uri);
}, 100);
});
var disableAddrListener = $rootScope.$on('Local/AddressIsUsed', function() {
2015-06-27 09:22:56 -07:00
self.setAddress(true);
2015-04-23 08:27:43 -07:00
});
var disableFocusListener = $rootScope.$on('Local/NewFocusedWalletReady', function() {
self.addr = null;
2015-04-23 22:42:10 -07:00
self.resetForm();
2016-02-18 12:55:57 -08:00
$scope.search = '';
2016-02-18 11:26:02 -08:00
2016-03-28 11:39:35 -07:00
if (profileService.focusedClient && profileService.focusedClient.isComplete()) {
self.setAddress();
self.setSendFormInputs();
}
2016-02-18 11:26:02 -08:00
$log.debug('Cleaning WalletHome Instance');
lodash.each(self, function(v, k) {
if (lodash.isFunction(v)) return;
if (!lodash.isUndefined(vanillaScope[k])) {
2016-02-23 04:54:35 -08:00
self[k] = vanillaScope[k];
return;
}
if (k == 'isRateAvailable') return;
delete self[k];
});
2015-04-23 22:42:10 -07:00
});
2015-04-23 11:19:30 -07:00
2015-04-28 14:00:49 -07:00
var disableResumeListener = $rootScope.$on('Local/Resume', function() {
2015-04-28 15:26:22 -07:00
// This is needed then the apps go to sleep
self.bindTouchDown();
2015-04-28 14:00:49 -07:00
});
2015-04-23 22:42:10 -07:00
var disableTabListener = $rootScope.$on('Local/TabChanged', function(e, tab) {
2015-04-28 12:58:40 -07:00
// This will slow down switch, do not add things here!
2015-04-23 22:42:10 -07:00
switch (tab) {
2015-04-23 11:19:30 -07:00
case 'receive':
2015-04-28 12:58:40 -07:00
// just to be sure we have an address
self.setAddress();
2015-04-23 11:19:30 -07:00
break;
2015-04-28 12:58:40 -07:00
case 'send':
self.resetError();
2015-04-23 11:19:30 -07:00
};
});
2015-04-23 08:27:43 -07:00
$scope.$on('$destroy', function() {
disableAddrListener();
disableScannerListener();
disablePaymentUriListener();
2015-04-23 11:19:30 -07:00
disableTabListener();
2015-04-23 22:42:10 -07:00
disableFocusListener();
2015-04-28 14:00:49 -07:00
disableResumeListener();
2016-02-03 08:35:52 -08:00
$rootScope.shouldHideMenuBar = false;
2015-04-23 08:27:43 -07:00
});
2016-08-10 08:14:35 -07:00
if (isCordova && StatusBar.isVisible) {
var backgroundColor = profileService.focusedClient ? profileService.focusedClient.backgroundColor : "#4B6178";
StatusBar.backgroundColorByHexString(backgroundColor);
}
2015-12-01 12:16:39 -08:00
this.onQrCodeScanned = function(data) {
if (data) go.send();
2015-12-01 12:16:39 -08:00
$rootScope.$emit('dataScanned', data);
};
2015-04-23 08:27:43 -07:00
rateService.whenAvailable(function() {
self.isRateAvailable = true;
$rootScope.$digest();
});
var getClipboard = function(cb) {
if (!isCordova || platformInfo.isWP) return cb();
window.cordova.plugins.clipboard.paste(function(value) {
var fc = profileService.focusedClient;
var Address = bitcore.Address;
var networkName = fc.credentials.network;
if (Address.isValid(value, networkName) && !$scope.newAddress) {
return cb(value);
}
});
};
var handleEncryptedWallet = function(client, cb) {
if (!walletService.isEncrypted(client)) return cb();
$rootScope.$emit('Local/NeedsPassword', false, function(err, password) {
if (err) return cb(err);
return cb(walletService.unlock(client, password));
});
};
2015-07-29 08:37:51 -07:00
var accept_msg = gettextCatalog.getString('Accept');
var cancel_msg = gettextCatalog.getString('Cancel');
var confirm_msg = gettextCatalog.getString('Confirm');
this.openAddressbookModal = function(wallets, address) {
$scope.wallets = wallets;
$scope.address = address;
$scope.self = self;
2015-06-27 09:48:25 -07:00
$ionicModal.fromTemplateUrl('views/modals/addressbook.html', {
scope: $scope
}).then(function(modal) {
$scope.addressbookModal = modal;
$scope.addressbookModal.show();
2015-06-27 09:48:25 -07:00
});
};
var GLIDERA_LOCK_TIME = 6 * 60 * 60;
2016-01-16 15:04:01 -08:00
// isGlidera flag is a security measure so glidera status is not
// only determined by the tx.message
this.openTxpModal = function(tx, copayers, isGlidera) {
2016-05-27 11:16:25 -07:00
$scope.self = self;
$scope.tx = tx;
$scope.copayers = copayers;
$scope.isGlidera = isGlidera;
$scope.error = null;
$scope.loading = null;
$scope.paymentExpired = null;
$scope.currentSpendUnconfirmed = configWallet.spendUnconfirmed;
$ionicModal.fromTemplateUrl('views/modals/txp-details.html', {
scope: $scope
}).then(function(modal) {
$scope.txpDetailsModal = modal;
$scope.txpDetailsModal.show();
2015-03-06 07:00:10 -08:00
});
};
2015-06-27 09:22:56 -07:00
this.setAddress = function(forceNew) {
2015-05-29 07:46:33 -07:00
self.addrError = null;
var client = profileService.focusedClient;
if (!client || !client.isComplete()) return;
2015-05-13 08:41:05 -07:00
2015-06-27 09:22:56 -07:00
// Address already set?
if (!forceNew && self.addr) {
2015-05-30 19:15:43 -07:00
return;
}
2015-06-27 09:22:56 -07:00
self.generatingAddress = true;
2015-04-23 08:27:43 -07:00
$timeout(function() {
addressService.getAddress(client.credentials.walletId, forceNew, function(err, addr) {
2015-06-27 09:22:56 -07:00
self.generatingAddress = false;
2015-08-12 07:49:13 -07:00
if (err) {
self.addrError = err;
2015-08-12 07:49:13 -07:00
} else {
if (addr)
self.addr = addr;
2015-04-23 08:27:43 -07:00
}
2015-06-27 09:22:56 -07:00
$scope.$digest();
2015-04-23 08:27:43 -07:00
});
});
};
2016-08-05 12:55:08 -07:00
this.copyToClipboard = function(addr, $event) {
var showPopover = function() {
$ionicPopover.fromTemplateUrl('views/includes/copyToClipboard.html', {
scope: $scope
}).then(function(popover) {
$scope.popover = popover;
$scope.popover.show($event);
});
$scope.close = function() {
$scope.popover.hide();
}
2016-08-05 12:55:08 -07:00
$timeout(function() {
$scope.popover.hide(); //close the popover after 0.7 seconds
}, 700);
2016-08-05 12:55:08 -07:00
$scope.$on('$destroy', function() {
$scope.popover.remove();
});
};
2015-04-23 08:27:43 -07:00
if (isCordova) {
2015-06-08 07:15:30 -07:00
window.cordova.plugins.clipboard.copy(addr);
2015-07-29 08:42:05 -07:00
window.plugins.toast.showShortCenter(gettextCatalog.getString('Copied to clipboard'));
} else if (platformInfo.isNW) {
2015-05-28 06:52:33 -07:00
nodeWebkit.writeToClipboard(addr);
2016-08-05 12:55:08 -07:00
showPopover($event);
2015-04-23 08:27:43 -07:00
}
};
this.shareAddress = function(addr) {
if (isCordova) {
window.plugins.socialsharing.share('bitcoin:' + addr, null, null, null);
}
};
2016-02-04 05:33:41 -08:00
// Send
2015-04-28 08:06:04 -07:00
2015-04-23 08:27:43 -07:00
this.resetError = function() {
2015-05-29 08:39:17 -07:00
this.error = this.success = null;
2015-04-23 08:27:43 -07:00
};
2015-04-28 12:08:43 -07:00
this.bindTouchDown = function(tries) {
var self = this;
tries = tries || 0;
if (tries > 5) return;
var e = document.getElementById('menu-walletHome');
2015-04-28 16:13:28 -07:00
if (!e) return $timeout(function() {
2015-04-28 12:08:43 -07:00
self.bindTouchDown(++tries);
}, 500);
// on touchdown elements
$log.debug('Binding touchstart elements...');
2015-12-02 13:20:22 -08:00
['hamburger', 'menu-walletHome', 'menu-send', 'menu-receive'].forEach(function(id) {
2015-04-28 12:08:43 -07:00
var e = document.getElementById(id);
if (e) e.addEventListener('touchstart', function() {
try {
event.preventDefault();
} catch (e) {};
2015-04-28 12:08:43 -07:00
angular.element(e).triggerHandler('click');
}, true);
2015-04-28 12:08:43 -07:00
});
}
2015-04-23 08:27:43 -07:00
this.hideMenuBar = lodash.debounce(function(hide) {
if (hide) {
$rootScope.shouldHideMenuBar = true;
} else {
$rootScope.shouldHideMenuBar = false;
2016-06-09 07:50:12 -07:00
}
$rootScope.$digest();
}, 100);
this.formFocus = function(what) {
if (isCordova && this.isWindowsPhoneApp) {
this.hideMenuBar(what);
2015-04-23 08:27:43 -07:00
}
var self = this;
if (isCordova && !this.isWindowsPhoneApp && what == 'address') {
getClipboard(function(value) {
if (value) {
document.getElementById("amount").focus();
$timeout(function() {
window.plugins.toast.showShortCenter(gettextCatalog.getString('Pasted from clipboard'));
self.setForm(value);
}, 100);
}
});
}
2015-04-23 08:27:43 -07:00
};
2015-04-28 12:58:40 -07:00
this.setSendFormInputs = function() {
2015-04-23 08:27:43 -07:00
var unitToSat = this.unitToSatoshi;
var satToUnit = 1 / unitToSat;
/**
* 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 $scope.__alternative;
},
set: function(newValue) {
$scope.__alternative = newValue;
if (self.isRateAvailable) {
2015-04-23 08:27:43 -07:00
$scope._amount = parseFloat((rateService.fromFiat(newValue, self.alternativeIsoCode) * satToUnit).toFixed(self.unitDecimals), 10);
2015-08-07 13:21:22 -07:00
} else {
$scope.__amount = null;
2015-04-23 08:27:43 -07:00
}
},
enumerable: true,
configurable: true
});
Object.defineProperty($scope,
"_amount", {
get: function() {
return $scope.__amount;
},
set: function(newValue) {
$scope.__amount = newValue;
if (self.isRateAvailable) {
2015-04-23 08:27:43 -07:00
$scope.__alternative = parseFloat((rateService.toFiat(newValue * self.unitToSatoshi, self.alternativeIsoCode)).toFixed(2), 10);
} else {
2015-08-07 13:21:22 -07:00
$scope.__alternative = null;
2015-04-23 08:27:43 -07:00
}
self.alternativeAmount = $scope.__alternative;
self.resetError();
},
enumerable: true,
configurable: true
});
Object.defineProperty($scope,
"_address", {
get: function() {
return $scope.__address;
},
set: function(newValue) {
$scope.__address = self.onAddressChange(newValue);
if ($scope.sendForm && $scope.sendForm.address.$valid) {
2015-10-22 14:43:32 -07:00
self.lockAddress = true;
}
2015-04-23 08:27:43 -07:00
},
enumerable: true,
configurable: true
});
var fc = profileService.focusedClient;
// ToDo: use a credential's (or fc's) function for this
this.hideNote = !fc.credentials.sharedEncryptingKey;
2015-04-23 08:27:43 -07:00
};
2015-05-12 06:48:00 -07:00
this.setSendError = function(err) {
2015-04-23 11:23:21 -07:00
var fc = profileService.focusedClient;
var prefix =
2015-08-13 12:47:10 -07:00
fc.credentials.m > 1 ? gettextCatalog.getString('Could not create payment proposal') : gettextCatalog.getString('Could not send payment');
2015-04-23 08:27:43 -07:00
2016-07-11 07:46:48 -07:00
this.error = bwcError.msg(err, prefix);
2016-02-01 11:18:44 -08:00
2015-04-23 08:27:43 -07:00
$timeout(function() {
$scope.$digest();
}, 1);
};
this.setAmount = function(amount, useAlternativeAmount) {
2016-07-20 06:37:12 -07:00
$scope.showAlternative = useAlternativeAmount;
self.fromInputAmount = true;
self.setForm(null, amount, null);
2016-07-19 13:10:40 -07:00
};
2015-12-23 13:05:22 -08:00
this.submitForm = function() {
2016-03-11 11:50:48 -08:00
if (!$scope._amount || !$scope._address) return;
var client = profileService.focusedClient;
2015-04-23 08:27:43 -07:00
var unitToSat = this.unitToSatoshi;
var currentSpendUnconfirmed = configWallet.spendUnconfirmed;
2015-08-12 07:49:13 -07:00
var outputs = [];
2016-01-20 10:25:54 -08:00
this.resetError();
if (isCordova && this.isWindowsPhoneApp)
$rootScope.shouldHideMenuBar = true;
2015-04-23 08:27:43 -07:00
var form = $scope.sendForm;
var comment = form.comment.$modelValue;
// ToDo: use a credential's (or fc's) function for this
if (comment && !client.credentials.sharedEncryptingKey) {
var msg = 'Could not add message to imported wallet without shared encrypting key';
$log.warn(msg);
return self.setSendError(gettext(msg));
}
2016-06-23 14:46:48 -07:00
if (form.amount.$modelValue * unitToSat > Number.MAX_SAFE_INTEGER) {
2016-06-21 08:58:59 -07:00
var msg = 'Amount too big';
$log.warn(msg);
return self.setSendError(gettext(msg));
};
2015-04-23 08:27:43 -07:00
$timeout(function() {
var paypro = self._paypro;
var address, amount;
address = form.address.$modelValue;
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
outputs.push({
'toAddress': address,
'amount': amount,
'message': comment
});
var txp = {};
if (!lodash.isEmpty(self.sendMaxInfo)) {
txp.sendMax = true;
txp.inputs = self.sendMaxInfo.inputs;
txp.fee = self.sendMaxInfo.fee;
2016-05-17 07:09:22 -07:00
} else {
txp.amount = amount;
}
txp.toAddress = address;
txp.outputs = outputs;
txp.message = comment;
txp.payProUrl = paypro ? paypro.url : null;
txp.excludeUnconfirmedUtxos = configWallet.spendUnconfirmed ? false : true;
txp.feeLevel = walletSettings.feeLevel || 'normal';
2016-06-13 07:44:58 -07:00
ongoingProcess.set('creatingTx', true);
walletService.createTx(client, txp, function(err, createdTxp) {
2016-06-13 07:44:58 -07:00
ongoingProcess.set('creatingTx', false);
2015-10-07 12:17:19 -07:00
if (err) {
2016-01-21 09:52:58 -08:00
return self.setSendError(err);
2015-10-07 12:17:19 -07:00
}
2015-07-24 08:11:07 -07:00
if (!client.canSign() && !client.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
ongoingProcess.set('sendingTx', true);
walletService.publishTx(client, createdTxp, function(err, publishedTxp) {
ongoingProcess.set('sendingTx', false);
if (err) {
return self.setSendError(err);
}
self.resetForm();
go.walletHome();
var type = txStatus.notify(createdTxp);
$scope.openStatusModal(type, createdTxp, function() {
return $scope.$emit('Local/TxProposalAction');
});
2015-07-24 08:11:07 -07:00
});
} else {
$rootScope.$emit('Local/NeedsConfirmation', createdTxp, function(accept) {
if (accept) self.confirmTx(createdTxp);
else self.resetForm();
});
}
2015-04-23 08:27:43 -07:00
});
2015-04-23 08:27:43 -07:00
}, 100);
2016-05-17 07:09:22 -07:00
};
2016-02-25 07:07:47 -08:00
this.confirmTx = function(txp) {
var client = profileService.focusedClient;
var self = this;
fingerprintService.check(client, function(err) {
if (err) {
return self.setSendError(err);
}
handleEncryptedWallet(client, function(err) {
if (err) {
return self.setSendError(err);
}
2016-06-13 07:44:58 -07:00
ongoingProcess.set('sendingTx', true);
walletService.publishTx(client, txp, function(err, publishedTxp) {
2016-06-14 13:14:37 -07:00
ongoingProcess.set('sendingTx', false);
if (err) {
return self.setSendError(err);
}
2016-06-13 07:44:58 -07:00
ongoingProcess.set('signingTx', true);
walletService.signTx(client, publishedTxp, function(err, signedTxp) {
2016-06-13 07:44:58 -07:00
ongoingProcess.set('signingTx', false);
walletService.lock(client);
if (err) {
$scope.$emit('Local/TxProposalAction');
return self.setSendError(
2016-05-17 07:09:22 -07:00
err.message ?
err.message :
gettext('The payment was created but could not be completed. Please try again from home screen'));
}
2016-05-17 07:09:22 -07:00
if (signedTxp.status == 'accepted') {
2016-06-13 07:44:58 -07:00
ongoingProcess.set('broadcastingTx', true);
walletService.broadcastTx(client, signedTxp, function(err, broadcastedTxp) {
2016-06-13 07:44:58 -07:00
ongoingProcess.set('broadcastingTx', false);
if (err) {
return self.setSendError(err);
}
self.resetForm();
go.walletHome();
var type = txStatus.notify(broadcastedTxp);
$scope.openStatusModal(type, broadcastedTxp, function() {
$scope.$emit('Local/TxProposalAction', broadcastedTxp.status == 'broadcasted');
});
});
} else {
self.resetForm();
go.walletHome();
var type = txStatus.notify(signedTxp);
$scope.openStatusModal(type, signedTxp, function() {
$scope.$emit('Local/TxProposalAction');
});
}
});
});
});
});
};
$scope.openStatusModal = function(type, txp, cb) {
var fc = profileService.focusedClient;
$scope.type = type;
$scope.tx = txFormatService.processTx(txp);
$scope.color = fc.backgroundColor;
$scope.cb = cb;
$ionicModal.fromTemplateUrl('views/modals/tx-status.html', {
scope: $scope,
animation: 'slide-in-up'
}).then(function(modal) {
$scope.txStatusModal = modal;
$scope.txStatusModal.show();
});
};
2016-05-26 06:40:20 -07:00
$scope.openSearchModal = function() {
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.self = self;
2016-05-17 07:09:22 -07:00
$ionicModal.fromTemplateUrl('views/modals/search.html', {
scope: $scope,
focusFirstInput: true
}).then(function(modal) {
$scope.searchModal = modal;
$scope.searchModal.show();
});
2016-05-17 07:09:22 -07:00
};
2016-08-08 12:00:26 -07:00
$scope.openCustomInputAmountModal = function(addr) {
2016-07-22 13:15:11 -07:00
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.self = self;
$scope.addr = addr;
$ionicModal.fromTemplateUrl('views/modals/customAmount.html', {
scope: $scope
}).then(function(modal) {
$scope.customAmountModal = modal;
$scope.customAmountModal.show();
});
};
2016-08-08 12:00:26 -07:00
$scope.openAmountModal = function(addr) {
2016-07-25 07:23:05 -07:00
if (isCordova)
2016-08-08 12:00:26 -07:00
$scope.openInputAmountModal(addr);
2016-07-25 07:23:05 -07:00
else
2016-08-08 12:00:26 -07:00
$scope.openCustomInputAmountModal(addr);
2016-07-25 07:23:05 -07:00
};
2016-07-19 08:00:58 -07:00
$scope.openInputAmountModal = function(addr) {
2016-07-18 10:50:39 -07:00
var fc = profileService.focusedClient;
$scope.color = fc.backgroundColor;
$scope.showAlternativeAmount = $scope.showAlternative || null;
if ($scope.showAlternativeAmount) {
$scope.amount = $scope.sendForm.alternative.$viewValue || null;
} else {
$scope.amount = $scope.sendForm.amount.$viewValue || null;
}
2016-07-18 10:50:39 -07:00
$scope.self = self;
2016-08-08 12:00:26 -07:00
$scope.addr = addr;
2016-07-18 10:50:39 -07:00
$ionicModal.fromTemplateUrl('views/modals/inputAmount.html', {
scope: $scope
}).then(function(modal) {
$scope.inputAmountModal = modal;
$scope.inputAmountModal.show();
});
};
2015-12-02 07:21:26 -08:00
this.setForm = function(to, amount, comment) {
var form = $scope.sendForm;
2015-04-23 08:27:43 -07:00
if (to) {
form.address.$setViewValue(to);
form.address.$isValid = true;
form.address.$render();
this.lockAddress = true;
}
if (amount) {
form.amount.$setViewValue("" + amount);
form.amount.$isValid = true;
form.amount.$render();
2016-08-10 12:55:33 -07:00
if (!this.fromInputAmount)
this.lockAmount = true;
this.fromInputAmount = false;
2015-04-23 08:27:43 -07:00
}
if (comment) {
form.comment.$setViewValue(comment);
form.comment.$isValid = true;
form.comment.$render();
}
};
2015-04-23 22:42:10 -07:00
this.resetForm = function() {
2015-04-23 08:27:43 -07:00
this.resetError();
this.sendMaxInfo = {};
2016-03-30 08:52:19 -07:00
if (this.countDown) $interval.cancel(this.countDown);
2015-04-23 08:27:43 -07:00
this._paypro = null;
this.lockAddress = false;
this.lockAmount = false;
this._amount = this._address = null;
var form = $scope.sendForm;
2015-08-13 10:54:53 -07:00
2015-04-23 08:27:43 -07:00
if (form && form.amount) {
form.amount.$pristine = true;
form.amount.$setViewValue('');
form.amount.$render();
form.comment.$setViewValue('');
form.comment.$render();
form.$setPristine();
if (form.address) {
form.address.$pristine = true;
form.address.$setViewValue('');
form.address.$render();
}
}
$timeout(function() {
$rootScope.$digest();
}, 1);
2016-02-04 05:33:41 -08:00
};
2015-04-23 08:27:43 -07:00
this.setFromPayPro = function(uri, cb) {
if (!cb) cb = function() {};
2015-04-23 11:23:21 -07:00
var fc = profileService.focusedClient;
2015-04-23 08:27:43 -07:00
if (isChromeApp) {
this.error = gettext('Payment Protocol not supported on Chrome App');
return cb(true);
2015-04-23 08:27:43 -07:00
}
var satToUnit = 1 / this.unitToSatoshi;
var self = this;
/// Get information of payment if using Payment Protocol
2016-06-13 07:44:58 -07:00
ongoingProcess.set('fetchingPayPro', true);
2015-04-23 08:27:43 -07:00
$log.debug('Fetch PayPro Request...', uri);
$timeout(function() {
fc.fetchPayPro({
payProUrl: uri,
}, function(err, paypro) {
2016-06-13 07:44:58 -07:00
ongoingProcess.set('fetchingPayPro', false);
2015-04-23 08:27:43 -07:00
if (err) {
$log.warn('Could not fetch payment request:', err);
2015-04-23 22:42:10 -07:00
self.resetForm();
2015-04-23 08:27:43 -07:00
var msg = err.toString();
if (msg.match('HTTP')) {
msg = gettext('Could not fetch payment information');
2015-04-23 08:27:43 -07:00
}
self.error = msg;
2015-09-18 08:07:51 -07:00
$timeout(function() {
$rootScope.$digest();
}, 1);
return cb(true);
}
if (!paypro.verified) {
self.resetForm();
2016-01-16 15:04:01 -08:00
$log.warn('Failed to verify payment protocol signatures');
2015-09-18 08:07:51 -07:00
self.error = gettext('Payment Protocol Invalid');
$timeout(function() {
$rootScope.$digest();
}, 1);
return cb(true);
2015-04-23 08:27:43 -07:00
}
self._paypro = paypro;
self.setForm(paypro.toAddress, (paypro.amount * satToUnit).toFixed(self.unitDecimals), paypro.memo);
2016-02-10 13:26:30 -08:00
_paymentTimeControl(paypro.expires);
return cb();
2015-04-23 08:27:43 -07:00
});
}, 1);
};
2016-03-22 12:37:55 -07:00
function _paymentTimeControl(expirationTime) {
self.paymentExpired = false;
setExpirationTime();
2016-03-30 08:52:19 -07:00
self.countDown = $interval(function() {
2016-03-22 12:37:55 -07:00
setExpirationTime();
}, 1000);
2016-02-10 13:26:30 -08:00
2016-03-22 12:37:55 -07:00
function setExpirationTime() {
2016-03-31 08:50:35 -07:00
var now = Math.floor(Date.now() / 1000);
if (now > expirationTime) {
2016-03-30 08:52:19 -07:00
setExpiredValues();
return;
2016-02-04 05:33:41 -08:00
}
2016-03-31 08:50:35 -07:00
var totalSecs = expirationTime - now;
var m = Math.floor(totalSecs / 60);
var s = totalSecs % 60;
2016-03-31 10:11:00 -07:00
self.remainingTimeStr = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2);
2016-03-22 12:37:55 -07:00
};
2016-02-10 13:26:30 -08:00
2016-03-30 08:52:19 -07:00
function setExpiredValues() {
2016-02-10 13:26:30 -08:00
self.paymentExpired = true;
2016-03-22 12:37:55 -07:00
self.remainingTimeStr = null;
2016-02-10 13:26:30 -08:00
self._paypro = null;
self.error = gettext('Cannot sign: The payment request has expired');
2016-03-30 08:52:19 -07:00
if (self.countDown) $interval.cancel(self.countDown);
2016-02-10 13:26:30 -08:00
};
2016-03-08 10:05:01 -08:00
};
2016-02-04 05:33:41 -08:00
2015-04-23 08:27:43 -07:00
this.setFromUri = function(uri) {
var self = this;
2015-04-23 08:27:43 -07:00
function sanitizeUri(uri) {
// Fixes when a region uses comma to separate decimals
var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
var match = regex.exec(uri);
if (!match || match.length === 0) {
return uri;
}
var value = match[0].replace(',', '.');
var newUri = uri.replace(regex, value);
return newUri;
};
var satToUnit = 1 / this.unitToSatoshi;
2015-09-29 08:51:08 -07:00
// URI extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin:\?r=[\w+]/).exec(uri)) {
uri = decodeURIComponent(uri.replace('bitcoin:?r=', ''));
this.setFromPayPro(uri, function(err) {
if (err) {
return err;
}
});
} else {
uri = sanitizeUri(uri);
2015-04-23 08:27:43 -07:00
if (!bitcore.URI.isValid(uri)) {
return uri;
}
var parsed = new bitcore.URI(uri);
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
2015-04-23 08:27:43 -07:00
var amount = parsed.amount ?
(parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0;
2015-04-23 08:27:43 -07:00
if (parsed.r) {
this.setFromPayPro(parsed.r, function(err) {
if (err && addr && amount) {
self.setForm(addr, amount, message);
return addr;
}
});
} else {
this.setForm(addr, amount, message);
return addr;
}
}
2015-04-23 08:27:43 -07:00
};
this.onAddressChange = function(value) {
this.resetError();
if (!value) return '';
if (this._paypro)
return value;
if (value.indexOf('bitcoin:') === 0) {
return this.setFromUri(value);
} else if (/^https?:\/\//.test(value)) {
return this.setFromPayPro(value);
} else {
return value;
}
};
2016-02-04 05:33:41 -08:00
// History
2015-04-23 08:27:43 -07:00
function strip(number) {
return (parseFloat(number.toPrecision(12)));
}
this.getUnitName = function() {
return this.unitName;
};
this.getAlternativeIsoCode = function() {
return this.alternativeIsoCode;
};
2016-05-20 07:37:13 -07:00
this.openTxModal = function(btx) {
var self = this;
2016-06-23 14:46:48 -07:00
$scope.btx = lodash.cloneDeep(btx);
2016-05-20 07:37:13 -07:00
$scope.self = self;
$ionicModal.fromTemplateUrl('views/modals/tx-details.html', {
scope: $scope,
hideDelay: 500
}).then(function(modal) {
$scope.txDetailsModal = modal;
$scope.txDetailsModal.show();
});
2016-02-09 13:48:53 -08:00
};
2015-04-23 08:27:43 -07:00
this.hasAction = function(actions, action) {
return actions.hasOwnProperty('create');
};
this.sendMax = function(availableBalanceSat) {
if (availableBalanceSat == 0) {
this.error = gettext("Cannot create transaction. Insufficient funds");
return;
}
var self = this;
var fc = profileService.focusedClient;
2016-02-22 14:56:53 -08:00
this.error = null;
2016-06-13 07:44:58 -07:00
ongoingProcess.set('calculatingFee', true);
2016-02-22 14:56:53 -08:00
2016-08-01 10:16:47 -07:00
$timeout(function() {
2016-08-01 10:16:47 -07:00
feeService.getCurrentFeeValue(function(err, feePerKb) {
ongoingProcess.set('calculatingFee', false);
if (err || !lodash.isNumber(feePerKb)) {
self.error = gettext('Could not get fee value');
return;
}
2016-08-01 10:16:47 -07:00
var opts = {};
opts.feePerKb = feePerKb;
opts.returnInputs = true;
var config = configService.getSync();
opts.excludeUnconfirmedUtxos = !config.wallet.spendUnconfirmed;
ongoingProcess.set('retrivingInputs', true);
2016-08-01 10:16:47 -07:00
fc.getSendMaxInfo(opts, function(err, resp) {
ongoingProcess.set('retrivingInputs', false);
2016-08-01 10:16:47 -07:00
if (err) {
self.error = err;
$scope.$apply();
return;
}
2016-08-01 10:16:47 -07:00
if (resp.amount == 0) {
self.error = gettext("Not enough funds for fee");
$scope.$apply();
return;
}
2016-08-01 10:16:47 -07:00
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees", {
fee: profileService.formatAmount(resp.fee) + ' ' + self.unitName
});
var warningMsg = verifyExcludedUtxos();
if (!lodash.isEmpty(warningMsg))
msg += '. \n' + warningMsg;
confirmDialog.show(msg, function(confirmed) {
if (confirmed) {
self.sendMaxInfo = resp;
var amount = parseFloat((resp.amount * self.satToUnit).toFixed(self.unitDecimals));
self.setForm(null, amount, null);
} else {
self.resetForm();
}
});
function verifyExcludedUtxos() {
var warningMsg = [];
if (resp.utxosBelowFee > 0) {
warningMsg.push(gettextCatalog.getString("Note: a total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", {
amountBelowFeeStr: profileService.formatAmount(resp.amountBelowFee) + ' ' + self.unitName
}));
}
if (resp.utxosAboveMaxSize > 0) {
warningMsg.push(gettextCatalog.getString("Note: a total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded", {
amountAboveMaxSizeStr: profileService.formatAmount(resp.amountAboveMaxSize) + ' ' + self.unitName
}));
}
return warningMsg.join('\n');
}
2016-08-01 10:16:47 -07:00
});
2015-12-23 13:05:22 -08:00
});
2016-08-01 10:16:47 -07:00
}, 10);
2015-08-12 07:49:13 -07:00
};
2015-06-19 11:00:27 -07:00
2015-06-27 09:22:56 -07:00
/* Start setup */
lodash.assign(self, vanillaScope);
2015-06-19 11:00:27 -07:00
2015-05-11 11:25:42 -07:00
this.bindTouchDown();
2015-09-14 11:01:49 -07:00
if (profileService.focusedClient) {
this.setAddress();
this.setSendFormInputs();
}
2015-03-06 07:00:10 -08:00
});