diff --git a/.vscode/launch.json b/.vscode/launch.json
deleted file mode 100644
index 29f920f4f..000000000
--- a/.vscode/launch.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
- "version": "0.2.0",
- "configurations": [
- {
- "type": "node",
- "request": "launch",
- "name": "Launch Program",
- "program": "${workspaceFolder}/www/index.html"
- }
- ]
-}
\ No newline at end of file
diff --git a/Gruntfile.js b/Gruntfile.js
index 7968f2510..4ac23e47a 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -71,7 +71,7 @@ module.exports = function(grunt) {
sign_android: {
// When the build log outputs "Built the following apk(s):", it seems to need the filename to start with "android-release".
// It looks like it simply lists all apk files starting with "android-release"
- command: 'rm -f platforms/android/build/outputs/apk/android-release-signed-*.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk',
+ command: 'rm -f platforms/android/build/outputs/apk/release/*-android-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk',
stdin: true,
},
sign_desktop_dist: {
diff --git a/README.md b/README.md
index ae2788217..75ced5808 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,3 @@
-This is a fork of the Bitcoin.com wallet to add additional features.
-Features included:
-
- - Zero fee transactions (only works for Bitcoin Cash). You will be asked for, if you want to send a transaction as zero fee on the confirmation page.
-
- ## Zero fee transactions:
- Because most network nodes on the Bitcoin Cash network don't relay zero fee txs, you will experience some strange issues, but don't worry: for me personally the Bitcoin.com pool has included all my zero fee transactions, but please beware that the receiver probably won't see your tx before it has been confirmed and please do also keep in mind, that the transactions coming after it won't confirm or be seen before the zero fee one has been confirmed.
-
- If you do already have a Bitcoin.com wallet, you need to create a new one to use this feature or change the wallet URL to: https://bws.freepages.dk/bws/api
-
- ## Disclaimer
-
- Please beware this is my personal experimental project. You are more than welcome to play with it, but I don't take any responsibility of loss of funds due to errors in the code, so please make sure you made a backup before running this software.
-
- ## Builds
-
- You can build the software yourself using the instructions below or use prebuilt binaries which can be found here (currently Windows and Linux only): https://ipfs.io/ipfs/QmR1DaS3QsDS48SzAWKUWFfmtMfJc4tgMtkSk3JFmuzewe
-
- ##
-
-
The Bitcoin.com wallet is a fork of the Copay Wallet (https://github.com/bitpay/copay).
The Bitcoin.com wallet is a secure bitcoin wallet platform for both desktop and mobile devices. It uses [Bitcore Wallet Service](https://github.com/Bitcoin-com/bitcore-wallet-service) (our fork of the [Bitpay Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)) (BWS) for peer synchronization and network interfacing.
diff --git a/app-template/bitcoincom/appConfig.json b/app-template/bitcoincom/appConfig.json
index 2eab6232d..e4d6c8a84 100644
--- a/app-template/bitcoincom/appConfig.json
+++ b/app-template/bitcoincom/appConfig.json
@@ -24,9 +24,9 @@
"windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c",
"pushSenderId": "1036948132229",
"description": "A Secure Bitcoin Wallet",
- "version": "5.1.3",
- "fullVersion": "5.1-rc2",
- "androidVersion": "501003",
+ "version": "5.0.2",
+ "fullVersion": "5.0-rc3",
+ "androidVersion": "500200",
"_extraCSS": "",
"_enabledExtensions": {
"coinbase": false,
diff --git a/app-template/config-template.xml b/app-template/config-template.xml
index 8686b7b36..b24b86fae 100644
--- a/app-template/config-template.xml
+++ b/app-template/config-template.xml
@@ -77,7 +77,7 @@
-
+
diff --git a/src/android/build-extras.gradle b/src/android/build-extras.gradle
index e7dd50572..c90145418 100644
--- a/src/android/build-extras.gradle
+++ b/src/android/build-extras.gradle
@@ -1,5 +1,6 @@
ext {
ANDROID_SUPPORT_V4_VERSION = '26.1.0'
+ ANDROID_SUPPORT_ANNOTATIONS_VERSION = '26.1.0'
}
configurations.all {
diff --git a/src/js/controllers/confirm.js b/src/js/controllers/confirm.js
index ae4bc7153..76950df73 100644
--- a/src/js/controllers/confirm.js
+++ b/src/js/controllers/confirm.js
@@ -517,8 +517,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (!lodash.isEmpty(warningMsg))
msg += '\n' + warningMsg;
- popupService.showAlert(null, msg, function() {});
-
+ popupService.showAlert(null, msg, function() {});
};
$scope.onWalletSelect = function(wallet) {
diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js
index ae30382fb..2311b2cb6 100644
--- a/src/js/controllers/review.controller.js
+++ b/src/js/controllers/review.controller.js
@@ -718,23 +718,14 @@ angular
if (tx.paypro)
startExpirationTimer(tx.paypro.expires);
- popupService.showConfirm(null, 'Do you want this transaction to be sent without a fee?', 'Yes', 'No', function(ok) {
- if(ok){
- tx.feeRate = 0;
- // tx.feeLevel = 'free';
- usingCustomFee = true;
- }
- updateTx(tx, vm.originWallet, {
- dryRun: true
- }, function(err) {
- $timeout(function() {
- $scope.$apply();
- }, 10);
-
- });
- });
-
+ updateTx(tx, vm.originWallet, {
+ dryRun: true
+ }, function(err) {
+ $timeout(function() {
+ $scope.$apply();
+ }, 10);
+ });
// setWalletSelector(tx.coin, tx.network, tx.amount, function(err) {
// if (err) {
@@ -787,7 +778,6 @@ angular
msg += '\n' + warningMsg;
popupService.showAlert(null, msg, function() {});
- //popupService.showConfirm(null, msg, null, null, function() {});
};
function statusChangeHandler(processName, showName, isOn) {
@@ -865,11 +855,7 @@ angular
}
var msg;
- // if (tx.feeLevel == 'free'){
- // tx.feeRate = 0;
- // }
- // else
- if (usingCustomFee) {
+ if (usingCustomFee) {
msg = gettextCatalog.getString('Custom');
tx.feeLevelName = msg;
} else if (usingMerchantFee) {
@@ -879,9 +865,7 @@ angular
} else {
tx.feeLevelName = feeService.feeOpts[tx.feeLevel];
tx.feeRate = feeRate;
-
}
-
getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) {
if (err) {
diff --git a/src/js/controllers/servicesController.js b/src/js/controllers/servicesController.js
index 0f9b6d4f8..62b13c041 100644
--- a/src/js/controllers/servicesController.js
+++ b/src/js/controllers/servicesController.js
@@ -1,6 +1,6 @@
'use strict';
-angular.module('copayApp.controllers').controller('servicesController', function(externalLinkService, $scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
+angular.module('copayApp.controllers').controller('servicesController', function($scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
$scope.hide = false;
configService.whenAvailable(function(config) {
@@ -20,8 +20,4 @@ angular.module('copayApp.controllers').controller('servicesController', function
}, 10);
};
- $scope.open = function(url) {
- externalLinkService.open(url, false);
- }
-
});
diff --git a/src/js/services/configService.js b/src/js/services/configService.js
index 6f2c0ec67..72cc4825f 100644
--- a/src/js/services/configService.js
+++ b/src/js/services/configService.js
@@ -18,7 +18,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
},
bwscash: {
- url: 'https://bws.freepages.dk/bws/api'
+ url: 'https://bwscash.bitcoin.com/bws/api'
},
download: {
diff --git a/src/js/services/feeService.js b/src/js/services/feeService.js
index 5189527b2..bba972b81 100644
--- a/src/js/services/feeService.js
+++ b/src/js/services/feeService.js
@@ -12,7 +12,6 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
normal: gettext('Normal'),
economy: gettext('Economy'),
superEconomy: gettext('Super Economy'),
-// free: gettext('No fee (works only for BCH)'),
custom: gettext('Custom')
};
@@ -32,7 +31,6 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
root.getFeeRate = function(coin, network, feeLevel, cb) {
if (feeLevel == 'custom') return cb();
-// if (feeLevel == 'free') return cb(null, 0);
network = network || 'livenet';
@@ -50,8 +48,8 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
})
});
}
- var feeRate = feeLevelRate.feePerKb;
+ var feeRate = feeLevelRate.feePerKb;
if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B');
diff --git a/src/js/services/send-flow.service.js b/src/js/services/send-flow.service.js
index e8be2e487..babf5096a 100644
--- a/src/js/services/send-flow.service.js
+++ b/src/js/services/send-flow.service.js
@@ -52,7 +52,7 @@ angular
// Detect some merchant that we know
if (payProData.memo.indexOf('eGifter') > -1) {
name = 'eGifter'
- } else if (paymentUrl.indexOf('https://bitpay.com') > -1) {
+ } else if (payProData.url.indexOf('https://bitpay.com') > -1) {
name = 'BitPay';
}
diff --git a/src/js/services/servicesService.js b/src/js/services/servicesService.js
index ddd9db394..316009957 100644
--- a/src/js/services/servicesService.js
+++ b/src/js/services/servicesService.js
@@ -5,7 +5,7 @@ angular.module('copayApp.services').factory('servicesService', function(configSe
name: 'shapeshift',
title: 'Shapeshift',
icon: 'icon-shapeshift',
- href: 'https://shapeshift.io/'
+ sref: 'tabs.shapeshift',
}];
root.register = function(serviceInfo) {
diff --git a/src/js/services/shapeShiftApiService.js b/src/js/services/shapeShiftApiService.js
index cc5fb0792..a90e587d4 100644
--- a/src/js/services/shapeShiftApiService.js
+++ b/src/js/services/shapeShiftApiService.js
@@ -24,7 +24,7 @@ var ShapeShift = (function() {
var parsedResponse = JP(xmlhttp.responseText);
cb.apply(null, [parsedResponse]);
} else {
- cb.apply(null, [new Error('Request Failed')])
+ cb.apply(null, [new Error('Request Failed')]);
}
}
};
diff --git a/src/js/services/wallet-history.service.js b/src/js/services/wallet-history.service.js
index 512a2d8b1..f114a396d 100644
--- a/src/js/services/wallet-history.service.js
+++ b/src/js/services/wallet-history.service.js
@@ -6,9 +6,9 @@
.module('bitcoincom.services')
.factory('walletHistoryService', walletHistoryService);
- function walletHistoryService(configService, storageService, lodash, $log, txFormatService) {
- //var PAGE_SIZE = 50;
- var PAGE_SIZE = 20; // For dev only
+ function walletHistoryService(storageService, lodash, $log, txFormatService) {
+ var PAGE_SIZE = 50;
+ //var PAGE_SIZE = 20; // For dev only
// How much to overlap on each end of the page, for mitigating inconsistent sort order.
var PAGE_OVERLAP_FRACTION = 0.2;
var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION);
@@ -28,8 +28,8 @@
function addEarlyTransactions(walletId, cachedTxs, newTxs) {
var cachedTxIndexFromId = {};
- cachedTxs.forEach(function forCachedTx(tx){
- cachedTxIndexFromId[tx.txid] = true;
+ cachedTxs.forEach(function forCachedTx(tx, txIndex){
+ cachedTxIndexFromId[tx.txid] = txIndex;
});
var confirmationsUpdated = false;
@@ -47,10 +47,17 @@
}
});
- var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
- console.log('overlappingTxFraction:', overlappingTxFraction);
-
- if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
+ var txsAreContinuous = false;
+ if (cachedTxs.length > 0) {
+ var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
+ console.log('overlappingTxsCount:', overlappingTxsCount);
+ console.log('overlappingTxFraction:', overlappingTxFraction);
+ txsAreContinuous = overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION;
+ } else {
+ txsAreContinuous = true;
+ }
+
+ if (txsAreContinuous) {
if (someTransactionsWereNew) {
saveTxHistory(walletId, cachedTxs);
} else if (confirmationsUpdated) {
@@ -61,7 +68,7 @@
return cachedTxs;
} else {
// We might be missing some txs.
- console.error('We might be missing some txs in the history.');
+ $log.error('We might be missing some txs in the history.');
// Our history is wrong, so remove it - we could instead, try to fetch data that was not so early.
storageService.removeTxHistory(walletId, function onRemoveTxHistory(){});
return [];
@@ -91,9 +98,17 @@
}
});
- var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
+ var txsAreContinuous = false;
+ if (cachedTxs.length > 0) {
+ var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
+ console.log('overlappingTxsCount:', overlappingTxsCount);
+ console.log('overlappingTxFraction:', overlappingTxFraction);
+ txsAreContinuous = overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION;
+ } else {
+ txsAreContinuous = true;
+ }
- if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
+ if (txsAreContinuous) {
if (someTransactionsWereNew) {
var allTxs = uniqueNewTxs.concat(cachedTxs);
saveTxHistory(walletId, allTxs);
@@ -106,6 +121,7 @@
}
} else {
// We might be missing some txs.
+ $log.error('We might be missing some txs in the history.');
// Our history is wrong, so just include the latest ones
saveTxHistory(walletId, newTxs);
return newTxs;
@@ -147,7 +163,6 @@
* @param {function(error, txs)} cb - txs is always an array, may be empty
*/
function getCachedTxHistory(walletId, cb) {
- console.log('txhistory updateLocalTxHistoryByPage()');
storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){
if (err) {
return cb(err, []);
@@ -230,7 +245,6 @@
}
function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) {
- console.log('txhistory updaetLocalTxHistoryByPage()');
if (flushCacheOnNew) {
fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){
if (err) {
diff --git a/src/js/services/wallet-history.service.spec.js b/src/js/services/wallet-history.service.spec.js
new file mode 100644
index 000000000..a6d9e12a7
--- /dev/null
+++ b/src/js/services/wallet-history.service.spec.js
@@ -0,0 +1,471 @@
+fdescribe('walletHistoryService', function(){
+ var history = [];
+ var historyStringFull;
+ var storageServiceMock;
+ var txFormatServiceMock;
+ var walletMock;
+ var walletHistoryService;
+
+
+ beforeEach(function(){
+ module('ngLodash');
+ module('bitcoincom.services');
+
+ storageServiceMock = jasmine.createSpyObj(['getTxHistory', 'removeTxHistory', 'setTxHistory']);
+ txFormatServiceMock = jasmine.createSpyObj(['processTx']);
+ txFormatServiceMock.processTx.and.callFake(function(coin, tx){
+ return tx;
+ });
+ walletMock = jasmine.createSpyObj(['getTxHistory']);
+
+ module(function($provide) {
+ $provide.value('storageService', storageServiceMock);
+ $provide.value('txFormatService', txFormatServiceMock);
+ });
+
+ inject(function($injector){
+ walletHistoryService = $injector.get('walletHistoryService');
+ });
+
+ for(var i = 0; i < 100; i++) {
+ history.push({
+ confirmations: i,
+ time: (Date.now() / 1000) - i,
+ txid: 'id' + i.toString()
+ });
+ }
+ historyStringFull = JSON.stringify(history);
+ });
+
+ it('getCachedHistory empty', function() {
+ var returnedErr;
+ var returnedHistory;
+ var walletIdForStorageGet = '';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+
+ cb(null, "[]");
+ });
+
+ walletHistoryService.getCachedTxHistory('wallet1234', function(err, txHistory){
+ returnedErr = err;
+ returnedHistory = txHistory;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet1234');
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(0);
+ });
+
+ it('getCachedHistory error from storage', function() {
+ var returnedErr;
+ var returnedHistory;
+ var walletIdForStorageGet = '';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+
+ cb(new Error('Something went wrong.'), null);
+ });
+
+ walletHistoryService.getCachedTxHistory('wallet12345', function(err, txHistory){
+ returnedErr = err;
+ returnedHistory = txHistory;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet12345');
+ expect(returnedErr.message).toBe('Something went wrong.');
+ expect(returnedHistory.length).toBe(0);
+ });
+
+ it('getCachedHistory page full', function() {
+ var returnedErr;
+ var returnedHistory;
+ var walletIdForStorageGet = '';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+
+ cb(null, JSON.stringify(history.slice(0, 50)));
+ });
+
+ walletHistoryService.getCachedTxHistory('wallet1234', function(err, txHistory){
+ returnedErr = err;
+ returnedHistory = txHistory;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet1234');
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(50);
+ });
+
+ it('updateLocalTxHistoryByPage, 2 in cache, getEarlier, keep cache, same 2 returned, so all transactions received.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedAllFetched;
+ var returnedErr;
+ var returnedHistory;
+ var walletIdForStorageGet;
+ walletMock.id = 'wallet456';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+
+ cb(null, JSON.stringify(history.slice(0, 2)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(0, 2));
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, false, false, function(err, txs, allFetched){
+ returnedErr = err;
+ returnedHistory = txs;
+ returnedAllFetched = allFetched;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet456');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(2);
+ expect(returnedAllFetched).toBe(true);
+ expect(storageServiceMock.setTxHistory.calls.any()).toBe(false);
+ });
+
+ it('updateLocalTxHistoryByPage, getEarlier, keep cache, sufficient overlap so saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedErr;
+ var returnedHistory;
+ var returnedAllFetched;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet67890';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+
+ cb(null, JSON.stringify(history.slice(0, 40)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(fetchSkip, fetchSkip + fetchLimit));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, false, false, function(err, txs, allFetched){
+ returnedErr = err;
+ returnedHistory = txs;
+ returnedAllFetched = allFetched;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet67890');
+ expect(walletIdForStorageSet).toBe('wallet67890');
+ expect(fetchSkip).toBe(30);
+ expect(fetchLimit).toBe(50);
+ expect(savedTxs.length).toBe(80);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(80);
+ expect(returnedAllFetched).toBe(false);
+ });
+
+ it('updateLocalTxHistoryByPage, cache empty, getLatest, do not flush cache, one new so saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, "[]");
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(0, 1));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(savedTxs.length).toBe(1);
+ expect(walletIdForStorageSet).toBe('wallet789');
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(1);
+ });
+
+ it('updateLocalTxHistoryByPage, cache empty, getLatest, do not flush cache, some new so saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, "[]");
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(0, 10));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(savedTxs.length).toBe(10);
+ expect(walletIdForStorageSet).toBe('wallet789');
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(10);
+ });
+
+ it('updateLocalTxHistoryByPage, some cachedTx, getLatest, do not flush cache, nothing new so nothing added.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var walletIdForStorageGet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, JSON.stringify(history.slice(0, 40)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, []);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(40);
+ expect(storageServiceMock.setTxHistory.calls.any()).toBe(false);
+ });
+
+ it('updateLocalTxHistoryByPage, some cachedTx, getLatest, do not flush cache, confirmations increased, saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, JSON.stringify(history.slice(2, 52)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ var historyWithHigherConfirmations = [];
+ for (var i = 0; i < 50; i++) {
+ historyWithHigherConfirmations.push({
+ confirmations: i >= 6 ? history[i].confirmations : history[i].confirmations + 1,
+ time: history[i].time,
+ txid: history[i].txid
+ });
+ }
+
+ cb(null, historyWithHigherConfirmations.slice(fetchSkip, fetchSkip + fetchLimit));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(walletIdForStorageSet).toBe('wallet789');
+ expect(savedTxs.length).toBe(52);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(52);
+ expect(returnedHistory[2].confirmations).toBe(3);
+
+ });
+
+ it('updateLocalTxHistoryByPage, some cachedTx, getLatest, do not flush cache, some new with insufficient overlap, so only new saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, JSON.stringify(history.slice(48, 78)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(fetchSkip, fetchSkip + fetchLimit));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(walletIdForStorageSet).toBe('wallet789');
+ expect(savedTxs.length).toBe(50);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(50);
+
+ });
+
+ it('updateLocalTxHistoryByPage, some cachedTx, getLatest, do not flush cache, some new with sufficient overlap so all saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageGet;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet789';
+
+ storageServiceMock.getTxHistory.and.callFake(function(walletId, cb){
+ walletIdForStorageGet = walletId;
+ cb(null, JSON.stringify(history.slice(42, 72)));
+ });
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(fetchSkip, fetchSkip + fetchLimit));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, false, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageGet).toBe('wallet789');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(walletIdForStorageSet).toBe('wallet789');
+ expect(savedTxs.length).toBe(72);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(72);
+
+ });
+
+ it('updateLocalTxHistoryByPage, getLatest, flush cache, some new so saved.', function(){
+ var fetchLimit;
+ var fetchSkip;
+ var returnedHistory;
+ var savedTxs;
+ var walletIdForStorageSet;
+ walletMock.id = 'wallet7890';
+
+ walletMock.getTxHistory.and.callFake(function(opts, cb){
+ fetchSkip = opts.skip;
+ fetchLimit = opts.limit;
+
+ cb(null, history.slice(0, fetchLimit));
+ });
+
+ storageServiceMock.setTxHistory.and.callFake(function(txs, walletId, cb){
+ savedTxs = txs;
+ walletIdForStorageSet = walletId;
+ cb(null);
+ });
+
+ walletHistoryService.updateLocalTxHistoryByPage(walletMock, true, true, function(err, txs){
+ returnedErr = err;
+ returnedHistory = txs;
+ });
+
+ expect(walletIdForStorageSet).toBe('wallet7890');
+ expect(fetchSkip).toBe(0);
+ expect(fetchLimit).toBe(50);
+ expect(savedTxs.length).toBe(50);
+ expect(returnedErr).toBeNull();
+ expect(returnedHistory.length).toBe(50);
+ expect(storageServiceMock.getTxHistory.calls.any()).toBe(false);
+ });
+
+});
\ No newline at end of file
diff --git a/www/css/main.css b/www/css/main.css
index 83c50fe17..8602ba35d 100644
--- a/www/css/main.css
+++ b/www/css/main.css
@@ -10083,7 +10083,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
.onboarding .button.button-white.button-standard,
.onboarding .button.button-green.button-standard,
.onboarding .button.button-assertive.button-standard, #shapeshift .button-shapeshift {
- width: 90%;
+ width: 85%;
max-width: 300px;
margin-left: auto;
margin-right: auto;
@@ -10195,7 +10195,6 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
.button {
border-radius: 6px; }
.button.button-full {
- border-radius: 0;
display: block; }
.button-green {
border-color: #FFF;
@@ -10264,8 +10263,8 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
color: #FFFFFF;
text-decoration: none; }
.button-white-outline.active, .button-white-outline.activated {
- border-color: #FFFFFF;
- background-color: #FFFFFF; }
+ border-color: #FFF;
+ background-color: #FAFAFA; }
.button-white-outline.button-outline {
border-color: #FFFFFF;
background: transparent;
@@ -10633,7 +10632,7 @@ qrcode {
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .primary-amount-display {
font-size: 2em; } }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
- font-size: 1.2em; }
+ font-size: 0.9em; }
@media (min-width: 375px) {
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
font-size: 1.3em; } }
@@ -10660,8 +10659,7 @@ qrcode {
line-height: 1em; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount .primary-amount-display {
margin-right: 5px;
- word-break: break-all;
- width: 100%; }
+ word-break: break-all; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .alternative-amount {
color: #6F6F70; }
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .switch-currencies {
@@ -10682,33 +10680,30 @@ qrcode {
justify-content: space-between;
margin: 0 14px; }
#view-amount .scroll-content .send-amount-extras .available-funds {
- color: #6F6F70;
- text-align: left; }
- #view-amount .scroll-content .send-amount-extras .change-currency {
- text-align: right; }
+ color: #6F6F70; }
#view-amount .scroll-content .send-amount-extras .warning {
color: #b7664d; }
- #view-amount .scroll-content .send-amount-extras .extra {
- flex: 1;
+ #view-amount .scroll-content .send-amount-extras .extra,
+ #view-amount .scroll-content .send-amount-extras button.extra {
+ /*display: flex;*/
+ flex: 0 1 auto; }
+ #view-amount .scroll-content .send-amount-extras button.extra {
+ background: none;
+ border: none;
+ color: #000;
+ font-family: 'ProximaNova';
+ font-size: 14px;
+ line-height: normal;
+ min-height: auto;
+ min-width: auto;
+ padding: 0; }
+ #view-amount .scroll-content .send-amount-extras .button .icon:before {
+ font-size: 14px;
line-height: normal; }
- #view-amount .scroll-content .send-amount-extras .extra .button {
- background: none;
- border: none;
- border-radius: 0;
- color: #000;
- font-family: 'ProximaNova';
- font-size: 14px;
- line-height: normal;
- min-height: auto;
- min-width: auto;
- padding: 0; }
- #view-amount .scroll-content .send-amount-extras .extra .button .icon:before {
- font-size: 14px;
- line-height: normal; }
- #view-amount .scroll-content .send-amount-extras .extra .button span {
- display: flex;
- align-items: center;
- justify-content: center; }
+ #view-amount .scroll-content .send-amount-extras .button span {
+ display: flex;
+ align-items: center;
+ justify-content: center; }
#view-amount .scroll-content .button.no-margin {
margin: 0; }
#view-amount .scroll-content .notification-warning {
@@ -10944,14 +10939,14 @@ qrcode {
#tab-home .buttons .button {
border: 2px solid;
border-radius: 47px;
- padding: 8px 2px 8px 2px;
+ padding: 0 15px 0 15px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
- min-height: 0;
- line-height: 19px; }
+ min-height: auto;
+ line-height: 36px; }
#tab-home .wallet-coin-logo {
vertical-align: middle;
@@ -11018,10 +11013,6 @@ qrcode {
#tab-home .release .title {
font-weight: 700;
color: #444; }
- #tab-home .release .release-notes {
- white-space: pre;
- white-space: pre-line;
- text-align: left; }
#tab-home .release .button {
width: 100%;
border: none; }
@@ -11034,14 +11025,6 @@ qrcode {
#tab-home .badge {
top: 11px; }
-.popup-update .popup-buttons {
- display: block; }
-
-.popup-update .popup-buttons .button {
- display: block;
- min-width: 100% !important;
- margin-top: 4px; }
-
#tab-receive .button-share {
color: #fff;
box-shadow: none;
@@ -11118,8 +11101,6 @@ qrcode {
#tab-receive .payment-received-container {
margin: 0 20px; }
- #tab-receive .payment-received-container svg {
- max-height: 400px; }
#tab-receive .payment-received-container .payment-received-amount {
font-size: 1.8em;
display: block;
@@ -11226,7 +11207,7 @@ qrcode {
margin: auto;
margin-top: 18px; }
#tab-send .send-wrapper .buttons .button {
- min-height: 65px;
+ height: 60px;
line-height: 16px;
margin-right: 0px;
width: 95%;
@@ -11303,9 +11284,7 @@ qrcode {
margin-top: 18px; }
#tab-send .sendTip .buttons .button {
font-weight: bold;
- font-size: 19px;
- line-height: 26px;
- padding: 8px 6px; }
+ font-size: 19px; }
#tab-send .sendTip .button-first-contact img {
height: 19px;
width: 19px;
@@ -11941,13 +11920,6 @@ qrcode {
#walletDetails .bp-content.status-bar {
margin-top: 20px;
margin-top: env(safe-area-inset-top); }
- #walletDetails .bp-content.collapse ion-content {
- margin-top: 40px; }
- #walletDetails .bp-content.collapse .amount__scale, #walletDetails .bp-content.collapse .amount__error {
- -webkit-transform: scale3d(0.5, 0.5, 0.5) translateY(0px);
- transform: scale3d(0.5, 0.5, 0.5) translateY(0px); }
- #walletDetails .bp-content.collapse .amount-alternative, #walletDetails .bp-content.collapse .send-receive-buttons, #walletDetails .bp-content.collapse .wallet-details-wallet-info {
- opacity: 0; }
#walletDetails .bar-header {
border: 0;
background: #eeb640; }
@@ -11960,12 +11932,9 @@ qrcode {
#walletDetails ion-content {
padding-top: 0;
top: 0;
- transition: all 0.25s ease-in-out;
- margin-top: 185px;
margin-bottom: 16px; }
- @media only screen and (max-height: 500px) {
- #walletDetails ion-content {
- margin-top: 165px; } }
+ #walletDetails ion-content.collapsible {
+ margin-top: 230px; }
#walletDetails ion-content .scroll {
background: #f8f8f9;
min-height: 300px; }
@@ -11996,45 +11965,38 @@ qrcode {
justify-content: space-evenly;
width: 100%;
position: absolute;
- bottom: 20px;
- transition: all 0.25s ease-in-out; }
+ bottom: 20px; }
#walletDetails .amount-wrapper .send-receive-buttons > .col {
padding: 5px 10px;
margin-bottom: 0; }
#walletDetails .amount-wrapper .send-receive-buttons .button {
border: 2px solid;
border-radius: 47px;
- padding: 6px 2px 6px 2px;
+ padding: 0 15px 0 15px;
text-align: center;
width: 100%;
max-width: 300px;
font-size: 19px;
font-weight: bolder;
- min-height: 0;
- line-height: 19px; }
+ min-height: auto;
+ line-height: 36px; }
#walletDetails .amount {
- align-items: center;
- color: #fff;
- display: block;
- height: 230px;
- justify-content: center;
- padding-top: 40px;
+ width: 100%;
text-align: center;
- transition: all 0.25s ease-in-out;
- width: 100%; }
- @media only screen and (max-height: 500px) {
- #walletDetails .amount {
- height: 210px; } }
+ color: #fff;
+ height: 230px;
+ padding-top: 40px;
+ display: block;
+ align-items: center;
+ justify-content: center; }
#walletDetails .amount__balance {
-webkit-transform: scale3d(1, 1, 1) translateY(45px);
- transform: scale3d(1, 1, 1) translateY(45px);
- transition: all 0.25s ease-in-out; }
+ transform: scale3d(1, 1, 1) translateY(45px); }
#walletDetails .amount__updating {
z-index: 999;
margin-top: -2.1rem; }
#walletDetails .amount-alternative {
- line-height: 36px;
- transition: all 0.25s ease-in-out; }
+ line-height: 36px; }
#walletDetails .amount__button-balance {
background-color: transparent;
border: 1px solid rgba(255, 255, 255, 0.25);
@@ -12044,8 +12006,7 @@ qrcode {
vertical-align: middle; }
#walletDetails .amount__error {
font-size: 14px;
- padding: 35px 20px;
- opacity: 1; }
+ padding: 35px 20px; }
#walletDetails .no-alternative {
padding-top: 45px; }
#walletDetails .item.item-footer {
@@ -12110,9 +12071,7 @@ a.item {
font-size: 0.9em; }
.loading-wallet svg {
- margin-top: 0;
- width: 16px;
- height: 16px; }
+ margin-top: 0; }
#advanced-settings .list .item:before {
display: block;
@@ -12166,9 +12125,7 @@ a.item {
margin-top: 18px; }
#shapeshift .empty-case .buttons .button {
font-weight: bold;
- font-size: 19px;
- line-height: 26px;
- padding: 8px 6px; }
+ font-size: 19px; }
#shapeshift .empty-case .button-first-contact img {
height: 19px;
width: 19px;
@@ -13855,7 +13812,6 @@ click-to-accept {
height: 92px;
width: 100%; }
click-to-accept .click-to-accept__button.button.button-primary.button-standard {
- border-radius: 0;
height: 100%;
max-width: 9999px;
width: 100%; }
@@ -13962,8 +13918,6 @@ slide-to-accept {
height: 92px;
width: 100%;
background: #494949; }
- slide-to-accept .slide .button {
- border-radius: 0; }
slide-to-accept .slide__listener {
height: 100%;
width: 100%;
@@ -15395,25 +15349,20 @@ log-options #check-bar .checkbox-icon {
#cash-scan a {
cursor: pointer; }
-#view-review slide-to-accept, #view-review slide-to-accept-success {
- margin-bottom: constant(safe-area-inset-bottom);
- /* iOS 11.0 */
- margin-bottom: env(safe-area-inset-bottom);
- /* iOS 11.2 */ }
-
-#view-review .fee-summary {
- bottom: 92px;
- bottom: calc(92px + constant(safe-area-inset-bottom));
- /* iOS 11.0 */
- bottom: calc(92px + env(safe-area-inset-bottom));
- /* iOS 11.2 */
- position: absolute; }
-
-#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
- box-shadow: none; }
-
-#view-review .warning {
- color: #b7664d; }
+#view-review {
+ background-color: #494949; }
+ #view-review slide-to-accept, #view-review slide-to-accept-success {
+ margin-bottom: constant(safe-area-inset-bottom);
+ /* iOS 11.0 */
+ margin-bottom: env(safe-area-inset-bottom);
+ /* iOS 11.2 */ }
+ #view-review .fee-summary {
+ position: absolute;
+ bottom: 92px; }
+ #view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
+ box-shadow: none; }
+ #view-review .warning {
+ color: #b7664d; }
.gravatar {
border-radius: 3px;
@@ -15446,7 +15395,6 @@ log-options #check-bar .checkbox-icon {
left: 13px;
top: 50%;
padding: 0;
- -webkit-transform: translate(0, -50%);
transform: translate(0, -50%); }
.item .item-content.item-content-compact {
@@ -15515,8 +15463,8 @@ ion-content.padded-bottom-cta-with-summary {
overflow: hidden;
text-overflow: ellipsis; }
.address-frame.expanded {
- white-space: normal;
- text-overflow: clip; }
+ white-space: pre-wrap;
+ word-break: break-all; }
.address-frame .prefix {
color: #000000; }
.address-frame .mid {
@@ -15559,13 +15507,13 @@ ion-content.padded-bottom-cta-with-summary {
transform: scale(1, 1); }
.fee-summary {
- background-color: #F2F2F2;
- box-sizing: border-box;
+ position: relative;
display: flex;
flex-direction: column;
+ width: 100%;
padding: 5px 12px 15px;
- position: relative;
- width: 100%; }
+ box-sizing: border-box;
+ background-color: #F2F2F2; }
.fee-summary:before {
content: '';
position: absolute;
@@ -15575,16 +15523,16 @@ ion-content.padded-bottom-cta-with-summary {
height: 15px;
background: linear-gradient(to bottom, rgba(242, 242, 242, 0) 0%, #f2f2f2 100%); }
.fee-summary .amount {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
width: 100%; }
- .fee-summary .amount .fee-fiat {
- display: inline; }
- .fee-summary .amount .fee-fiat.positive {
- color: #70955F; }
- .fee-summary .amount .fee-fiat.negative {
- color: #C24633; }
+ .fee-summary .amount .fee-fiat.positive {
+ color: #70955F; }
+ .fee-summary .amount .fee-fiat.negative {
+ color: #C24633; }
.fee-summary .amount .fee-crypto {
- color: #A7A7A7;
- float: right; }
+ color: #A7A7A7; }
.formatted-amount {
display: inline-block; }
@@ -15609,9 +15557,6 @@ ion-content.padded-bottom-cta-with-summary {
margin-left: 5px;
text-transform: uppercase; }
-.wallet-balance-directive {
- display: inline-block; }
-
/* This is for rules that don't yet have a home.
* Our goal is to delete this file. Search the regex: /class=".*CLASS.*?"/
*/
diff --git a/www/views/includes/services.html b/www/views/includes/services.html
index 05e6dcf2d..f57cd1004 100644
--- a/www/views/includes/services.html
+++ b/www/views/includes/services.html
@@ -6,14 +6,7 @@