diff --git a/Gruntfile.js b/Gruntfile.js index 907f8c706..21b799ac7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -124,8 +124,7 @@ module.exports = function(grunt) { 'lib/socket.io-client/socket.io.js', 'lib/sjcl.js', 'lib/ios-imagefile-megapixel/megapix-image.js', - 'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js', - 'lib/zeroclipboard/ZeroClipboard.min.js' + 'lib/qrcode-decoder-js/lib/qrcode-decoder.min.js' ], dest: 'lib/vendors.js' }, diff --git a/bower.json b/bower.json index c77f71035..60023c9d9 100644 --- a/bower.json +++ b/bower.json @@ -20,7 +20,6 @@ "angular-moment": "~0.7.1", "socket.io-client": ">=1.0.0", "mousetrap": "1.4.6", - "zeroclipboard": "~1.3.5", "ng-idle": "*", "inherits": "~0.0.1", "angular-load": "0.2.0", diff --git a/js/controllers/send.js b/js/controllers/send.js index b1816ee87..c9e9aa774 100644 --- a/js/controllers/send.js +++ b/js/controllers/send.js @@ -3,7 +3,7 @@ var bitcore = require('bitcore'); var preconditions = require('preconditions').singleton(); angular.module('copayApp.controllers').controller('SendController', - function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) { + function($scope, $rootScope, $window, $timeout, $modal, isMobile, notification, controllerUtils, rateService) { controllerUtils.redirIfNotComplete(); @@ -13,6 +13,7 @@ angular.module('copayApp.controllers').controller('SendController', $rootScope.title = 'Send'; $scope.loading = false; + $scope.error = $scope.success = null; var satToUnit = 1 / w.settings.unitToSatoshi; $scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit; $scope.unitToBtc = w.settings.unitToSatoshi / bitcore.util.COIN; @@ -145,15 +146,7 @@ angular.module('copayApp.controllers').controller('SendController', if (w.requiresMultipleSignatures()) { $scope.loading = false; - var message = 'The transaction proposal has been created'; - if (merchantData) { - if (merchantData.pr.ca) { - message += ' This payment protocol transaction' + ' has been verified through ' + merchantData.pr.ca + '.'; - } - message += ' Message from server: ' + merchantData.pr.pd.memo; - message += ' For merchant: ' + merchantData.pr.pd.payment_url; - } - notification.success('Success', message); + notification.success('Success', 'The transaction proposal created'); $scope.loadTxs(); } else { w.sendTx(ntxid, function(txid, merchantData) { @@ -163,10 +156,10 @@ angular.module('copayApp.controllers').controller('SendController', if (merchantData.pr.ca) { message += ' This payment protocol transaction' + ' has been verified through ' + merchantData.pr.ca + '.'; } - message += ' Message from server: ' + merchantData.pr.pd.memo; - message += ' For merchant: ' + merchantData.pr.pd.payment_url; + message += merchantData.pr.pd.memo; + message += ' Merchant: ' + merchantData.pr.pd.payment_url; } - notification.success('Transaction broadcasted', message); + $scope.success = 'Transaction broadcasted' + message; } else { $scope.error = 'There was an error sending the transaction'; } @@ -352,7 +345,6 @@ angular.module('copayApp.controllers').controller('SendController', $scope.copyAddress = function(address) { $scope.address = address; - $anchorScroll(); }; $scope.openAddressBookModal = function() { @@ -363,7 +355,6 @@ angular.module('copayApp.controllers').controller('SendController', $scope.submitAddressBook = function(form) { if (form.$invalid) { - notification.error('Form Error', 'Please complete required fields'); return; } var entry = { @@ -391,6 +382,7 @@ angular.module('copayApp.controllers').controller('SendController', errorMsg = e.message; } + // TODO change this notifications if (errorMsg) { notification.error('Error', errorMsg); } else { @@ -409,14 +401,15 @@ angular.module('copayApp.controllers').controller('SendController', $scope.send = function(ntxid, cb) { + $scope.error = $scope.success = null; $scope.loading = true; $rootScope.txAlertCount = 0; w.sendTx(ntxid, function(txid, merchantData) { if (!txid) { - $scope.error = 'There was an error sending the transaction'; + notification.error('Error', 'There was an error sending the transaction'); } else { if (!merchantData) { - notification.success('Transaction broadcasted', 'Transaction id: ' + txid); + notification.success('Success', 'Transaction broadcasted!'); } else { var message = 'Transaction ID: ' + txid; if (merchantData.pr.ca) { @@ -424,7 +417,7 @@ angular.module('copayApp.controllers').controller('SendController', } message += ' Message from server: ' + merchantData.ack.memo; message += ' For merchant: ' + merchantData.pr.pd.payment_url; - notification.success('Transaction sent', message); + notification.success('Success', 'Transaction sent' + message); } } @@ -435,15 +428,15 @@ angular.module('copayApp.controllers').controller('SendController', $scope.sign = function(ntxid) { $scope.loading = true; + $scope.error = $scope.success = null; try { w.sign(ntxid); } catch (e) { - $scope.error = 'There was an error signing the transaction'; + notification.error('Error','There was an error signing the transaction'); $scope.loadTxs(); return; } - $scope.error = undefined; var p = w.txProposals.getTxProposal(ntxid); if (p.builder.isFullySigned()) { @@ -465,16 +458,15 @@ angular.module('copayApp.controllers').controller('SendController', }; $scope.clearMerchant = function(callback) { - var scope = $scope; // TODO: Find a better way of detecting // whether we're in the Send scope or not. - if (!scope.sendForm || !scope.sendForm.address) { + if (!$scope.sendForm || !$scope.sendForm.address) { delete $rootScope.merchant; $rootScope.merchantError = false; if (callback) callback(); return; } - var val = scope.sendForm.address.$viewValue || ''; + var val = $scope.sendForm.address.$viewValue || ''; var uri; // If we're setting the domain, ignore the change. if ($rootScope.merchant && $rootScope.merchant.domain && val === $rootScope.merchant.domain) { @@ -491,8 +483,8 @@ angular.module('copayApp.controllers').controller('SendController', } if (!uri || !uri.merchant) { delete $rootScope.merchant; - scope.sendForm.amount.$setViewValue(''); - scope.sendForm.amount.$render(); + $scope.sendForm.amount.$setViewValue(''); + $scope.sendForm.amount.$render(); if (callback) callback(); if ($rootScope.$$phase !== '$apply' && $rootScope.$$phase !== '$digest') { $rootScope.$apply(); @@ -501,10 +493,10 @@ angular.module('copayApp.controllers').controller('SendController', }; $scope.onChanged = function() { - var scope = $scope; - var value = scope.address || ''; + var value = $scope.address || ''; var uri; + $scope.error = $scope.success = null; // If we're setting the domain, ignore the change. if ($rootScope.merchant && $rootScope.merchant.domain && value === $rootScope.merchant.domain) { return; @@ -528,28 +520,28 @@ angular.module('copayApp.controllers').controller('SendController', } }; - notification.info('Fetching Payment', - 'Retrieving Payment Request from ' + uri.merchant); - - scope.loading = true; + $scope.fetchingURL = uri.merchant; + $scope.loading = true; apply(); var timeout = setTimeout(function() { timeout = null; - scope.loading = false; - scope.sendForm.address.$setViewValue(''); - scope.sendForm.address.$render(); - scope.sendForm.address.$isValid = false; - notification.error('Error', 'Payment server timed out.'); + $scope.fetchingURL = null; + $scope.loading = false; + $scope.sendForm.address.$setViewValue(''); + $scope.sendForm.address.$render(); + $scope.sendForm.address.$isValid = false; + $scope.error = 'Payment server timed out'; apply(); }, 10 * 1000); // Payment Protocol URI (BIP-72) - scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) { + $scope.wallet.fetchPaymentTx(uri.merchant, function(err, merchantData) { if (!timeout) return; clearTimeout(timeout); - scope.loading = false; + $scope.loading = false; + $scope.fetchingURL = null; apply(); var balance = $rootScope.availableBalance; @@ -561,28 +553,29 @@ angular.module('copayApp.controllers').controller('SendController', if (err) { if (err.amount) { - scope.sendForm.amount.$setViewValue(+err.amount / w.settings.unitToSatoshi); - scope.sendForm.amount.$render(); - scope.sendForm.amount.$isValid = false; - scope.notEnoughAmount = true; + $scope.sendForm.amount.$setViewValue(+err.amount / w.settings.unitToSatoshi); + $scope.sendForm.amount.$render(); + $scope.sendForm.amount.$isValid = false; + $scope.notEnoughAmount = true; $rootScope.merchantError = true; - var lastAddr = scope.sendForm.address.$viewValue; - var unregister = scope.$watch('address', function() { - if (scope.sendForm.address.$viewValue !== lastAddr) { + var lastAddr = $scope.sendForm.address.$viewValue; + var unregister = $scope.$watch('address', function() { + if ($scope.sendForm.address.$viewValue !== lastAddr) { delete $rootScope.merchantError; - scope.sendForm.amount.$setViewValue(''); - scope.sendForm.amount.$render(); + $scope.sendForm.amount.$setViewValue(''); + $scope.sendForm.amount.$render(); unregister(); apply(); } }); } else { - scope.sendForm.address.$setViewValue(''); - scope.sendForm.address.$render(); + $scope.sendForm.address.$setViewValue(''); + $scope.sendForm.address.$render(); } - scope.sendForm.address.$isValid = false; + $scope.sendForm.address.$isValid = false; + copay.logger.error(err); - notification.error('Error', err.message || 'Bad payment server.'); + $scope.error = 'Could not fetch payment request'; apply(); return; @@ -593,18 +586,18 @@ angular.module('copayApp.controllers').controller('SendController', merchantData.unitTotal = (+merchantData.total / w.settings.unitToSatoshi) + ''; merchantData.expiration = new Date( - merchantData.pr.pd.expires * 1000).toISOString(); + merchantData.pr.pd.expires * 1000); merchantData.domain = domain; $rootScope.merchant = merchantData; - scope.sendForm.address.$setViewValue(domain); - scope.sendForm.address.$render(); - scope.sendForm.address.$isValid = true; + $scope.sendForm.address.$setViewValue(domain); + $scope.sendForm.address.$render(); + $scope.sendForm.address.$isValid = true; - scope.sendForm.amount.$setViewValue(merchantData.unitTotal); - scope.sendForm.amount.$render(); - scope.sendForm.amount.$isValid = true; + $scope.sendForm.amount.$setViewValue(merchantData.unitTotal); + $scope.sendForm.amount.$render(); + $scope.sendForm.amount.$isValid = true; // If the address changes to a non-payment-protocol one, // delete the `merchant` property from the scope. @@ -613,11 +606,6 @@ angular.module('copayApp.controllers').controller('SendController', }); apply(); - - notification.info('Payment Request', - 'Server is requesting ' + merchantData.unitTotal + - ' ' + w.settings.unitName + - '.' + ' Message: ' + merchantData.pr.pd.memo); }); }; }); diff --git a/js/directives.js b/js/directives.js index 7bf829def..e24748362 100644 --- a/js/directives.js +++ b/js/directives.js @@ -5,6 +5,24 @@ var Address = bitcore.Address; var bignum = bitcore.Bignum; var preconditions = require('preconditions').singleton(); + +function selectText(element) { + var doc = document; + if (doc.body.createTextRange) { // ms + var range = doc.body.createTextRange(); + range.moveToElementText(element); + range.select(); + } else if (window.getSelection) { + var selection = window.getSelection(); + var range = doc.createRange(); + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + + } +} + + angular.module('copayApp.directives') .directive('validAddress', ['$rootScope', @@ -162,10 +180,14 @@ angular.module('copayApp.directives') var contact = scope.wallet.addressBook[address]; if (contact && !contact.hidden) { element.append(contact.label); - attrs['tooltip'] = attrs.address; + element.attr('tooltip',attrs.address); } else { element.append(address); } + + element.bind('click', function() { + selectText(element[0]); + }); } }; }) @@ -296,39 +318,19 @@ angular.module('copayApp.directives') }; }) .directive('clipCopy', function() { - ZeroClipboard.config({ - moviePath: './lib/zeroclipboard/ZeroClipboard.swf', - trustedDomains: ['*'], - allowScriptAccess: 'always', - forceHandCursor: true - }); - return { restric: 'A', scope: { clipCopy: '=clipCopy' }, link: function(scope, elm) { - var client = new ZeroClipboard(elm); + // TODO this does not work (FIXME) + elm.attr('tooltip','Press Ctrl+C to Copy'); + elm.attr('tooltip-placement','top'); - client.on('load', function(client) { - - client.on('datarequested', function(client) { - client.setText(scope.clipCopy); - }); - - client.on('complete', function(client, args) { - elm.removeClass('btn-copy').addClass('btn-copied').html('Copied!'); - setTimeout(function() { - elm.addClass('btn-copy').removeClass('btn-copied').html(''); - }, 1000); - }); + elm.bind('click', function() { + selectText(elm[0]); }); - client.on('wrongflash noflash', function() { - elm.removeClass('btn-copy').html(''); - ZeroClipboard.destroy(); - }); - } }; }); diff --git a/js/models/Insight.js b/js/models/Insight.js index b62181117..ddf408ed0 100644 --- a/js/models/Insight.js +++ b/js/models/Insight.js @@ -256,8 +256,8 @@ Insight.prototype.broadcast = function(rawtx, cb) { this.requestPost('/api/tx/send', { rawtx: rawtx }, function(err, res, body) { - if (err || res.statusCode != 200) cb(err || body); - cb(null, body ? body.txid : null); + if (err || res.statusCode != 200) return cb(err || body); + return cb(null, body ? body.txid : null); }); }; diff --git a/js/models/TxProposal.js b/js/models/TxProposal.js index ae9de2dd9..fba658556 100644 --- a/js/models/TxProposal.js +++ b/js/models/TxProposal.js @@ -12,7 +12,7 @@ var buffertools = bitcore.buffertools; var preconditions = require('preconditions').instance(); var VERSION = 1; -var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version', 'comment']; +var CORE_FIELDS = ['builderObj', 'inputChainPaths', 'version', 'comment', 'paymentProtocolURL']; function TxProposal(opts) { @@ -37,6 +37,7 @@ function TxProposal(opts) { this.comment = opts.comment || null; this.readonly = opts.readonly || null; this.merchant = opts.merchant || null; + this.paymentProtocolURL = opts.paymentProtocolURL || null; this._sync(); } diff --git a/js/models/Wallet.js b/js/models/Wallet.js index debb6b82a..22c98aa81 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -426,14 +426,79 @@ Wallet.prototype._getKeyMap = function(txp) { Wallet.prototype._checkSentTx = function(ntxid, cb) { var txp = this.txProposals.get(ntxid); var tx = txp.builder.build(); - var txid = bitcore.util.formatHashFull(tx.getHash()); + var txHex = tx.serialize().toString('hex'); + + //Use calcHash NOT getHash which could be cached. + var txid = bitcore.util.formatHashFull(tx.calcHash()); this.blockchain.getTransaction(txid, function(err, tx) { if (err) return cb(false); return cb(txid); }); }; +Wallet.prototype._processTxProposalSeen = function(ntxid) { + var txp = this.txProposals.get(ntxid); + if (!txp.getSeen(this.getMyCopayerId())) { + txp.setSeen(this.getMyCopayerId()); + this.sendSeen(ntxid); + } +}; + + +Wallet.prototype._processTxProposalSent = function(ntxid, cb) { + var self = this; + var txp = this.txProposals.get(ntxid); + + this._checkSentTx(ntxid, function(txid) { + if (txid) { + if (!txp.getSent()) { + txp.setSent(txid); + } + } + self.emitAndKeepAlive('txProposalsUpdated'); + if (cb) return cb(null, txid); + }); +}; + + +Wallet.prototype._processTxProposalPayPro = function(mergeInfo, cb) { + var self = this; + var txp = mergeInfo.txp; + var isNew = mergeInfo.new; + var ntxid = mergeInfo.ntxid; + + if (!isNew || !txp.paymentProtocolURL) + return cb(); + + log.info('Received a Payment Protocol TX Proposal'); + self.fetchPaymentTx(txp.paymentProtocolURL, function(err, merchantData) { + if (err) return cb(err); + txp.merchant = merchantData; + return cb(); + }); +}; + +Wallet.prototype._processIncomingTxProposal = function(mergeInfo, cb) { + if (!mergeInfo) return cb(); + var self = this; + + self._processTxProposalPayPro(mergeInfo, function(err) { + if (err) return cb(err); + + self._processTxProposalSeen(mergeInfo.ntxid); + + var tx = mergeInfo.txp.builder.build(); + if (tx.isComplete()) + self._processTxProposalSent(mergeInfo.ntxid); + else if (mergeInfo.hasChanged) { + self.sendTxProposal(mergeInfo.ntxid); + self.emitAndKeepAlive('txProposalsUpdated'); + } + return cb(); + }); +}; + /** * @desc * Handles a 'TXPROPOSAL' network message @@ -454,34 +519,20 @@ Wallet.prototype._onTxProposal = function(senderId, data) { m.newCopayer = m.txp.setCopayers(senderId, keyMap); } catch (e) { log.error('Corrupt TX proposal received from:', senderId, e.toString()); + if (m && m.ntxid) + this.txProposals.deleteOne(m.ntxid); m = null; } - if (m) { - if (!m.txp.getSeen(this.getMyCopayerId())) { - m.txp.setSeen(this.getMyCopayerId()); - this.sendSeen(m.ntxid); + self._processIncomingTxProposal(m, function(err) { + if (err) { + log.error('Corrupt TX proposal received from:', senderId, err.toString()); + if (m && m.ntxid) + self.txProposals.deleteOne(m.ntxid); + m = null; } - - var tx = m.txp.builder.build(); - if (tx.isComplete()) { - this._checkSentTx(m.ntxid, function(txid) { - if (txid) { - if (!m.txp.getSent()) { - m.txp.setSent(txid); - self.emitAndKeepAlive('txProposalsUpdated'); - } - } - }); - } else { - if (m.hasChanged) { - this.sendTxProposal(m.ntxid); - } - } - - this.emitAndKeepAlive('txProposalsUpdated'); - } - this._processProposalEvents(senderId, m); + self._processProposalEvents(senderId, m); + }); }; /** @@ -1420,16 +1471,18 @@ Wallet.prototype.sign = function(ntxid) { Wallet.prototype.sendTx = function(ntxid, cb) { var txp = this.txProposals.get(ntxid); - if (txp.merchant) { - return this.sendPaymentTx(ntxid, cb); - } var tx = txp.builder.build(); if (!tx.isComplete()) throw new Error('Tx is not complete. Can not broadcast'); + log.debug('Wallet:' + this.id + ' Broadcasting Transaction'); + + if (txp.merchant) { + return this.sendPaymentTx(ntxid, cb); + } + var scriptSig = tx.ins[0].getScript(); var size = scriptSig.serialize().length; - var txHex = tx.serialize().toString('hex'); log.debug('Wallet:' + this.id + ' Raw transaction: ', txHex); @@ -1446,10 +1499,7 @@ Wallet.prototype.sendTx = function(ntxid, cb) { return cb(txid); } else { log.info('Wallet:' + self.getName() + '. Sent failed. Checking if the TX was sent already'); - self._checkSentTx(ntxid, function(txid) { - if (txid) - self.emitAndKeepAlive('txProposalsUpdated'); - + self._processTxProposalSent(ntxid, function(err, txid) { return cb(txid); }); } @@ -1640,6 +1690,7 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) { if (!unspent || !unspent.length) { return cb(new Error('No unspent outputs available.')); } + try { self.createPaymentTxSync(options, merchantData, safeUnspent); } catch (e) { @@ -1765,7 +1816,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { view[i] = pay[i]; } - return Wallet.request({ + var postInfo = { method: 'POST', url: txp.merchant.pr.pd.payment_url, headers: { @@ -1780,7 +1831,9 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { // be the ArrayBuffer, now you send the View instead). data: view, responseType: 'arraybuffer' - }) + }; + + return Wallet.request(postInfo) .success(function(data, status, headers, config) { data = PayPro.PaymentACK.decode(data); var ack = new PayPro(); @@ -1790,9 +1843,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { .error(function(data, status, headers, config) { log.debug('Sending to server was not met with a returned tx.'); log.debug('XHR status: ' + status); - return self._checkSentTx(ntxid, function(txid) { - if (txid) - self.emitAndKeepAlive('txProposalsUpdated'); + self._processTxProposalSent(ntxid, function(err, txid) { return cb(txid, txp.merchant); }); }); @@ -1822,10 +1873,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) { tx = payment.message.transactions[0]; if (!tx) { log.debug('Sending to server was not met with a returned tx.'); - return this._checkSentTx(ntxid, function(txid) { + return this._processTxProposalSeen(ntxid, function(err, txid) { log.debug('[Wallet.js.1613:txid:%s]', txid); - if (txid) - self.emitAndKeepAlive('txProposalUpdated'); return cb(txid, txp.merchant); }); } @@ -1841,7 +1890,7 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) { log.debug('It is not returning a copy of the transaction.'); } - var txid = tx.getHash().toString('hex'); + var txid = tx.calcHash().toString('hex'); var txHex = tx.serialize().toString('hex'); log.debug('Raw transaction: ', txHex); @@ -1855,11 +1904,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) { self.emitAndKeepAlive('txProposalsUpdated'); return cb(txid, txp.merchant); } else { - log.debug('Sent failed. Checking if the TX was sent already'); - self._checkSentTx(ntxid, function(txid) { - if (txid) - self.emitAndKeepAlive('txProposalsUpdated'); - + log.debug('PayPro Sent failed. Checking if the TX was sent already'); + self._processTxProposalSent(ntxid, function(err, txid) { return cb(txid, txp.merchant); }); } @@ -1951,6 +1997,10 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) }); var selectedUtxos = b.getSelectedUnspent(); + if (selectedUtxos.length > TX_MAX_INS) + throw new Error('BIG: Resulting TX is too big:' + selectedUtxos.length + ' inputs. Aborting'); + + var inputChainPaths = selectedUtxos.map(function(utxo) { return pkr.pathForAddress(utxo.address); }); @@ -1971,6 +2021,12 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) if (!tx.countInputSignatures(0)) throw new Error('Could not sign generated tx'); + var txSize = tx.getSize(); + if (txSize / 1024 > TX_MAX_SIZE_KB) + throw new Error('BIG: Resulting TX is too big ' + txSize + ' bytes. Aborting'); + + + var me = {}; me[myId] = now; var meSeen = {}; @@ -1984,8 +2040,10 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent) createdTs: now, builder: b, comment: options.memo, - merchant: merchantData + merchant: merchantData, + paymentProtocolURL: options.uri, })); + return ntxid; }; @@ -2187,7 +2245,7 @@ Wallet.prototype.subscribeToAddresses = function() { var addrInfo = this.publicKeyRing.getAddressesInfo(); this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr')); - log.debug('Subscribed to ' + addrInfo.length + ' addresses'); //TODO + log.debug('Subscribed to ' + addrInfo.length + ' addresses'); }; /** @@ -2386,7 +2444,7 @@ Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) var ntxid; try { ntxid = self.createTxSync(toAddress, amountSatStr, comment, safeUnspent, opts); - log.debug('TX Created: ntxid', ntxid); //TODO + log.debug('TX Created: ntxid', ntxid); } catch (e) { return cb(e); } diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index b4f59e995..48a626e81 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -362,6 +362,11 @@ angular.module('copayApp.services') var res = w.getPendingTxProposals(); _.each(res.txs, function(tx) { root.computeAlternativeAmount(w, tx); + if (tx.merchant) { + var url = tx.merchant.request_url; + var domain = /^(?:https?)?:\/\/([^\/:]+).*$/.exec(url)[1]; + tx.merchant.domain = domain; + } }); $rootScope.txps = res.txs; if ($rootScope.pendingTxCount < res.pendingForUs) { diff --git a/test/Wallet.js b/test/Wallet.js index 1c8c38ab6..f0d3466b9 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1533,6 +1533,8 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); + w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, @@ -1574,6 +1576,7 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, @@ -1616,6 +1619,7 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, @@ -1649,6 +1653,7 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, @@ -1682,6 +1687,7 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, @@ -1712,6 +1718,7 @@ describe('Wallet model', function() { }, }; + w.txProposals.get = sinon.stub().returns(txp); w.txProposals.merge = sinon.stub().returns({ ntxid: 1, txp: txp, diff --git a/views/home.html b/views/home.html index d59484531..ec882102a 100644 --- a/views/home.html +++ b/views/home.html @@ -46,6 +46,8 @@ You can import your current wallets after creating your profile + +

Sign in to Copay

diff --git a/views/homeWallet.html b/views/homeWallet.html index 1875b814b..1c317b6fa 100644 --- a/views/homeWallet.html +++ b/views/homeWallet.html @@ -40,15 +40,10 @@
-

{{$root.addrInfos[0].addressStr}}

+

{{$root.addrInfos[0].addressStr}}

-
- -
diff --git a/views/includes/transaction.html b/views/includes/transaction.html index 7a8878c90..aaff45bcb 100644 --- a/views/includes/transaction.html +++ b/views/includes/transaction.html @@ -20,7 +20,20 @@ + +
+
+

+ {{tx.merchant.pr.pd.memo}} +

+ Expires {{tx.merchant.pr.pd.expires * 1000 | amTimeAgo }} + [{{tx.merchant.domain}}] + {{tx.merchant.pr.ca}} + Untrusted +

+
+ diff --git a/views/receive.html b/views/receive.html index c82c2443b..6ec2c7fe3 100644 --- a/views/receive.html +++ b/views/receive.html @@ -12,9 +12,8 @@
- + - change
diff --git a/views/send.html b/views/send.html index ce23b133f..621b46a12 100644 --- a/views/send.html +++ b/views/send.html @@ -13,7 +13,7 @@

{{$root.title}}

-
+

{{error|translate}}

+

+ + {{success|translate}} +

+ +
+
+

+ + Fetching payment +

+

From {{fetchingURL}} +

+

This is a payment protocol transaction

- Send to: - {{$root.merchant.domain}} + {{$root.merchant.pr.pd.memo}}

+

- Total amount for this transaction: {{amount + defaultFee |noFractionNumber}} {{$root.wallet.settings.unitName}} - + {{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }} - - + + (Including fee of {{defaultFee|noFractionNumber}} {{$root.wallet.settings.unitName}}) - -

+

- Server Says: - {{$root.merchant.pr.pd.memo}} -

-

- Certificate: - {{$root.merchant.pr.ca}} - Untrusted -

-

- Payment Expiration: - {{$root.merchant.expiration}} -

+ Expires {{$root.merchant.expiration | amTimeAgo }} + [{{$root.merchant.domain}}] + {{$root.merchant.pr.ca}} + Untrusted
@@ -179,7 +184,7 @@
-
+
1 BTC = {{alternativeConversionRate|noFractionNumber:2}} {{alternativeIsoCode}}