diff --git a/i18n/po/template.pot b/i18n/po/template.pot index 5483ce40b..f61012914 100644 --- a/i18n/po/template.pot +++ b/i18n/po/template.pot @@ -3865,4 +3865,12 @@ msgstr "" #: src/js/controllers/tab-scan.js:121 msgid "Testnet is not supported." +msgstr "" + +#: www/views/includes/incomingDataMenu.html:81 +msgid "URL" +msgstr "" + +#: www/views/includes/incomingDataMenu.html:90 +msgid "Open in web browser" msgstr "" \ No newline at end of file diff --git a/src/js/services/bitcoin-uri.service.js b/src/js/services/bitcoin-uri.service.js index 0dd57dbf2..f2f17fd9d 100644 --- a/src/js/services/bitcoin-uri.service.js +++ b/src/js/services/bitcoin-uri.service.js @@ -93,6 +93,7 @@ returns: { amount: '', + bareUrl: '', coin: '', copayInvitation: '', isValid: false, @@ -143,26 +144,29 @@ var preColonLower = colonSplit[1].toLowerCase(); if (preColonLower === 'bitcoin') { parsed.coin = 'btc'; - addressAndParams = colonSplit[2]; + addressAndParams = colonSplit[2].trim(); console.log('Is btc'); } else if (/^(?:bitcoincash)|(?:bitcoin-cash)$/.test(preColonLower)) { parsed.coin = 'bch'; parsed.test = false; - addressAndParams = colonSplit[2]; + addressAndParams = colonSplit[2].trim(); console.log('Is bch'); } else if (/^(?:bchtest)$/.test(preColonLower)) { parsed.coin = 'bch'; parsed.testnet = true; - addressAndParams = colonSplit[2]; + addressAndParams = colonSplit[2].trim(); console.log('Is bch'); } else if (colonSplit[2] === '') { // No colon and no coin specifier. - addressAndParams = colonSplit[1]; + addressAndParams = colonSplit[1].trim(); console.log('No prefix.'); + } else if (/^https?$/.test(colonSplit[1])) { + addressAndParams = trimmed; + } else { // Something with a colon in the middle that we don't recognise return parsed; @@ -253,6 +257,7 @@ var privateKeyForUncompressedPublicKeyTestnetRe = /^9[1-9A-HJ-NP-Za-km-z]{50}$/; var privateKeyForCompressedPublicKeyRe = /^[KL][1-9A-HJ-NP-Za-km-z]{51}$/; var privateKeyForCompressedPublicKeyTestnetRe = /^[c][1-9A-HJ-NP-Za-km-z]{51}$/; + var urlRe = /^https?:\/\/.+/; var bitpayAddrMainnet = bitpayAddrOnMainnet(address); var cashAddrTestnet = cashAddrOnTestnet(addressLowerCase); @@ -322,6 +327,10 @@ } else if (privateKeyEncryptedRe.test(address)) { parsed.privateKey = { encrypted: address }; parsed.isValid = true; + + } else if (urlRe.test(address)) { + parsed.bareUrl = trimmed; + parsed.isValid = true; } } else { diff --git a/src/js/services/bitcoin-uri.service.spec.js b/src/js/services/bitcoin-uri.service.spec.js index 0e4a2ba31..61c6f6fa3 100644 --- a/src/js/services/bitcoin-uri.service.spec.js +++ b/src/js/services/bitcoin-uri.service.spec.js @@ -140,6 +140,16 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(false); }); + it('Bitcoin uri with space', function() { + + var parsed = bitcoinUriService.parse('bitcoin: 19cPoKU5ZazY8NkLEsxK7drBqJnpGkax3d'); + + expect(parsed.isValid).toBe(true); + expect(parsed.coin).toBe('btc'); + expect(parsed.publicAddress.legacy).toBe('19cPoKU5ZazY8NkLEsxK7drBqJnpGkax3d'); + expect(parsed.testnet).toBe(false); + }); + it('Bitpay without prefix', function() { var parsed = bitcoinUriService.parse('CJoRov8TirekvajiimQpb5Hk95evA7H2Yz'); @@ -220,6 +230,27 @@ fdescribe('bitcoinUriService', function() { expect(parsed.testnet).toBe(false); }); + it('cashAddr with space', function() { + + var parsed = bitcoinUriService.parse('bitcoincash: qpar9ldle8z6alcwgclejdhc24ha2xrg0szs5802ce'); + + expect(parsed.isValid).toBe(true); + expect(parsed.coin).toBe('bch'); + expect(parsed.publicAddress.cashAddr).toBe('qpar9ldle8z6alcwgclejdhc24ha2xrg0szs5802ce'); + expect(parsed.testnet).toBe(false); + }); + + + it('cashAddr with space on testnet', function() { + + var parsed = bitcoinUriService.parse('bchtest: qqjxkmtaxk4nv6w9h5ht2fjcj9c7ruh0fu7cnxsx5j'); + + expect(parsed.isValid).toBe(true); + expect(parsed.coin).toBe('bch'); + expect(parsed.publicAddress.cashAddr).toBe('qqjxkmtaxk4nv6w9h5ht2fjcj9c7ruh0fu7cnxsx5j'); + expect(parsed.testnet).toBe(true); + }); + it('cashAddr without prefix', function() { var parsed = bitcoinUriService.parse('qqen2y3l28dpk0dzsag8w027ds96u7z4pc0uxtl0nq'); @@ -343,11 +374,20 @@ fdescribe('bitcoinUriService', function() { expect(parsed.isValid).toBe(false); }); - it('URL only', function() { + it('URL only, http', function() { - var parsed = bitcoinUriService.parse('https://www.google.com'); + var parsed = bitcoinUriService.parse('http://paperwallet.bitcoin.com'); - expect(parsed.isValid).toBe(false); + expect(parsed.isValid).toBe(true); + expect(parsed.bareUrl).toBe('http://paperwallet.bitcoin.com'); + }); + + it('URL only, https with query', function() { + + var parsed = bitcoinUriService.parse('https://purse.io/?one=two&three=four'); + + expect(parsed.isValid).toBe(true); + expect(parsed.bareUrl).toBe('https://purse.io/?one=two&three=four'); }); }); \ No newline at end of file diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index 84bc7995d..53ed66ef4 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('incomingData', function(bitcoinUriService, $log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) { +angular.module('copayApp.services').factory('incomingData', function(externalLinkService, bitcoinUriService, $log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) { var root = {}; @@ -226,20 +226,23 @@ angular.module('copayApp.services').factory('incomingData', function(bitcoinUriS ); return true; // Plain URL - } else if (/^https?:\/\//.test(data)) { - payproService.getPayProDetails(data, coin, function(err, details) { - if (err) { - if ($state.includes('tabs.scan')) { - root.showMenu({ - data: data, - type: 'url' - }); - } - return; - } - handlePayPro(details); - return true; - }); + } else if (allParsed.bareUrl) { + + if ($state.includes('tabs.scan')) { + root.showMenu({ + data: allParsed.bareUrl, + type: 'url' + }); + } else { + externalLinkService.open( + allParsed.bareUrl, + true, + gettextCatalog.getString('Open in web browser'), + allParsed.bareUrl + ); + } + return true; + // Plain Address } else if (bitcore.Address.isValid(data, 'livenet') || bitcore.Address.isValid(data, 'testnet')) { if ($state.includes('tabs.scan')) { diff --git a/www/views/includes/incomingDataMenu.html b/www/views/includes/incomingDataMenu.html index ca4b78dfc..1d66b616a 100644 --- a/www/views/includes/incomingDataMenu.html +++ b/www/views/includes/incomingDataMenu.html @@ -76,4 +76,28 @@ +
+
+
URL
+
+
+ {{data}} +
+
+
+ + +
Open in web browser
+ +
+ + +
Copy to clipboard
+ +
+ + Cancel + +
+