fix merge conflicts

This commit is contained in:
Marty Alcala 2016-11-15 11:01:30 -05:00
commit 64d81c55ba
47 changed files with 1035 additions and 406 deletions

View file

@ -145,6 +145,12 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
updateHistoryFromCache(function() {
self.update();
});
bitpayCardService.getBitpayDebitCards(function(err, cards) {
if (err) return;
$scope.card = lodash.find(cards, function(card) {
return card.eid == $scope.cardId;
});
});
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('thanksController', function($scope, $stateParams, $timeout, $log, platformInfo, configService, storageService) {
angular.module('copayApp.controllers').controller('completeController', function($scope, $stateParams, $timeout, $log, platformInfo, configService, storageService) {
$scope.score = parseInt($stateParams.score);
$scope.skipped = $stateParams.skipped == 'false' ? false : true;
$scope.isCordova = platformInfo.isCordova;
@ -31,7 +31,12 @@ angular.module('copayApp.controllers').controller('thanksController', function($
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
storageService.setRateCardFlag('true', function() {});
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = JSON.parse(info);
feedbackInfo.sent = true;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {});
});
if (!$scope.isCordova) return;
window.plugins.socialsharing.available(function(isAvailable) {

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('rateAppStoreController', function($scope, $state, $stateParams, externalLinkService, configService, gettextCatalog, platformInfo) {
angular.module('copayApp.controllers').controller('rateAppController', function($scope, $state, $stateParams, lodash, externalLinkService, configService, gettextCatalog, platformInfo, feedbackService, ongoingProcess) {
$scope.score = parseInt($stateParams.score);
var isAndroid = platformInfo.isAndroid;
var isIOS = platformInfo.isIOS;
@ -8,14 +8,29 @@ angular.module('copayApp.controllers').controller('rateAppStoreController', func
var config = configService.getSync();
$scope.skip = function() {
$state.go('feedback.thanks', {
score: $scope.score,
skipped: true
var dataSrc = {
"Email": lodash.values(config.emailFor)[0] || ' ',
"Feedback": ' ',
"Score": $stateParams.score
};
ongoingProcess.set('sendingFeedback', true);
feedbackService.send(dataSrc, function(err) {
ongoingProcess.set('sendingFeedback', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not send feedback'));
return;
}
$state.go('feedback.complete', {
score: $stateParams.score,
skipped: true
});
});
};
$scope.sendFeedback = function() {
$state.go('feedback.sendFeedback', {
$state.go('feedback.send', {
score: $scope.score
});
};

View file

@ -3,6 +3,7 @@
angular.module('copayApp.controllers').controller('rateCardController', function($scope, $state, $timeout, gettextCatalog, platformInfo, storageService) {
$scope.isCordova = platformInfo.isCordova;
$scope.score = 0;
$scope.goFeedbackFlow = function() {
if ($scope.isModal) {
@ -10,11 +11,11 @@ angular.module('copayApp.controllers').controller('rateCardController', function
$scope.rateModal.remove();
}
if ($scope.isCordova && $scope.score == 5) {
$state.go('feedback.rateAppStore', {
$state.go('feedback.rateApp', {
score: $scope.score
});
} else {
$state.go('feedback.sendFeedback', {
$state.go('feedback.send', {
score: $scope.score
});
}
@ -49,13 +50,17 @@ angular.module('copayApp.controllers').controller('rateCardController', function
$scope.rateModal.hide();
$scope.rateModal.remove();
} else {
storageService.setRateCardFlag('true', function() {
$scope.hideRateCard.value = true;
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = JSON.parse(info);
feedbackInfo.sent = true;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {
$scope.showRateCard.value = false;
});
});
}
$timeout(function() {
$scope.$apply();
})
}, 100);
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('sendFeedbackController', function($scope, $state, $log, $stateParams, gettextCatalog, popupService, configService, lodash, feedbackService, ongoingProcess) {
angular.module('copayApp.controllers').controller('sendController', function($scope, $state, $log, $stateParams, gettextCatalog, popupService, configService, lodash, feedbackService, ongoingProcess) {
$scope.score = parseInt($stateParams.score);
switch ($scope.score) {
case 1:
@ -42,7 +42,7 @@ angular.module('copayApp.controllers').controller('sendFeedbackController', func
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not send feedback'));
return;
}
$state.go('feedback.thanks', {
$state.go('feedback.complete', {
score: $stateParams.score,
skipped: skip
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('headController',
function($scope, $window, $log, glideraService) {
function($scope, $window, $log) {
$scope.appConfig = $window.appConfig;
$log.info('Running head controller:' + $window.appConfig.nameCase)
});

View file

@ -19,13 +19,7 @@ angular.module('copayApp.controllers').controller('welcomeController', function(
$log.debug('Creating profile');
profileService.createProfile(function(err) {
if (err) $log.warn(err);
setProfileCreationTime();
});
};
function setProfileCreationTime() {
var now = moment().unix() * 1000 + 24 * 60 * 60 * 1000;
storageService.setProfileCreationTime(now, function() {});
};
});

View file

@ -3,16 +3,16 @@
angular.module('copayApp.controllers').controller('preferencesBitpayCardController',
function($scope, $state, $timeout, $ionicHistory, bitpayCardService, popupService, gettextCatalog) {
$scope.remove = function() {
$scope.remove = function(card) {
var msg = gettextCatalog.getString('Are you sure you would like to remove your BitPay Card account from this device?');
popupService.showConfirm(null, msg, null, null, function(res) {
if (res) remove();
if (res) remove(card);
});
};
var remove = function() {
bitpayCardService.remove(function() {
$ionicHistory.removeBackView();
var remove = function(card) {
bitpayCardService.remove(card, function() {
$ionicHistory.clearHistory();
$timeout(function() {
$state.go('tabs.home');
}, 100);
@ -22,7 +22,7 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll
$scope.$on("$ionicView.beforeEnter", function(event, data) {
bitpayCardService.getBitpayDebitCards(function(err, data) {
if (err) return;
$scope.bitpayCards = data.cards;
$scope.bitpayCards = data;
});
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('tabHomeController',
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, $window, bitpayCardService, startupService, addressbookService) {
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, gettextCatalog, lodash, popupService, ongoingProcess, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, $window, bitpayCardService, startupService, addressbookService, feedbackService) {
var wallet;
var listeners = [];
var notifications = [];
@ -13,7 +13,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isNW = platformInfo.isNW;
$scope.hideRateCard = {};
$scope.showRateCard = {};
$scope.$on("$ionicView.afterEnter", function() {
startupService.ready();
@ -37,13 +37,37 @@ angular.module('copayApp.controllers').controller('tabHomeController',
});
}
storageService.getProfileCreationTime(function(error, time) {
var now = moment().unix() * 1000;
storageService.getRateCardFlag(function(error, value) {
$scope.hideRateCard.value = (value == 'true' || (time - now) > 0) ? true : false;
});
storageService.getFeedbackInfo(function(error, info) {
if (!info) {
initFeedBackInfo();
} else {
var feedbackInfo = JSON.parse(info);
//Check if current version is greater than saved version
var currentVersion = window.version;
var savedVersion = feedbackInfo.version;
var isVersionUpdated = feedbackService.isVersionUpdated(currentVersion, savedVersion);
if (!isVersionUpdated) {
initFeedBackInfo();
return;
}
var now = moment().unix();
var timeExceeded = (now - feedbackInfo.time) >= 24 * 60 * 60;
$scope.showRateCard.value = timeExceeded && !feedbackInfo.sent;
$timeout(function() {
$scope.$apply();
});
}
});
function initFeedBackInfo() {
var feedbackInfo = {};
feedbackInfo.time = moment().unix();
feedbackInfo.version = window.version;
feedbackInfo.sent = false;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {
$scope.showRateCard.value = false;
});
};
});
$scope.$on("$ionicView.enter", function(event, data) {
@ -102,12 +126,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
});
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
@ -178,8 +196,11 @@ angular.module('copayApp.controllers').controller('tabHomeController',
lodash.each($scope.wallets, function(wallet) {
walletService.getStatus(wallet, {}, function(err, status) {
if (err) {
if (err === 'WALLET_NOT_REGISTERED') wallet.error = gettextCatalog.getString('Wallet not registered');
else wallet.error = gettextCatalog.getString('Could not update');;
$log.error(err);
} else {
wallet.error = null;
wallet.status = status;
}
if (++j == i) {
@ -231,7 +252,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
var services = ['AmazonGiftCards', 'BitpayCard', 'BuyAndSell'];
lodash.each(services, function(service) {
storageService.getNextStep(service, function(err, value) {
$scope.externalServices[service] = value ? true : false;
$scope.externalServices[service] = value == 'true' ? true : false;
if (++i == services.length) return cb();
});
});
@ -252,7 +273,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.bitpayCards = null;
return;
}
$scope.bitpayCards = data.cards;
$scope.bitpayCards = data;
});
bitpayCardService.getBitpayDebitCardsHistory(null, function(err, data) {
if (err) return;

View file

@ -1,9 +1,11 @@
'use strict';
angular.module('copayApp.controllers').controller('tabReceiveController', function($scope, $timeout, $log, $ionicModal, $state, $ionicHistory, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService) {
angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError) {
var listeners = [];
$scope.isCordova = platformInfo.isCordova;
$scope.isNW = platformInfo.isNW;
$scope.walletAddrs = {};
$scope.shareAddress = function(addr) {
if ($scope.generatingAddress) return;
@ -18,14 +20,21 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.generatingAddress = true;
walletService.getAddress($scope.wallet, forceNew, function(err, addr) {
$scope.generatingAddress = false;
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
if (err) popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
$scope.addr = addr;
if ($scope.walletAddrs[$scope.wallet.id]) $scope.walletAddrs[$scope.wallet.id] = addr;
$timeout(function() {
$scope.$apply();
}, 10);
});
};
$scope.loadAddresses = function(wallet, index) {
walletService.getAddress(wallet, false, function(err, addr) {
$scope.walletAddrs[wallet.id] = addr;
});
}
$scope.goCopayers = function() {
$ionicHistory.removeBackView();
$ionicHistory.nextViewOptions({
@ -67,6 +76,24 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
});
};
$scope.setWallet = function(index) {
$scope.wallet = $scope.wallets[index];
$scope.walletIndex = index;
if ($scope.walletAddrs[$scope.walletIndex].addr) $scope.addr = $scope.walletAddrs[$scope.walletIndex].addr;
else $scope.setAddress(false);
}
$scope.isActive = function(index) {
return $scope.wallets[index] == $scope.wallet;
}
$scope.walletPosition = function(index) {
if (index == $scope.walletIndex) return 'current';
if (index < $scope.walletIndex) return 'prev';
if (index > $scope.walletIndex) return 'next';
}
$scope.$on('Wallet/Changed', function(event, wallet) {
if (!wallet) {
$log.debug('No wallet provided');
@ -77,14 +104,57 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
return;
}
$scope.wallet = wallet;
$scope.generatingAddress = false;
$log.debug('Wallet changed: ' + wallet.name);
$scope.walletIndex = lodash.findIndex($scope.wallets, function(wallet) {
return wallet.id == $scope.wallet.id;
});
if (!$scope.walletAddrs[wallet.id]) $scope.setAddress(false);
else $scope.addr = $scope.walletAddrs[wallet.id];
$timeout(function() {
$scope.setAddress(false);
$scope.$apply();
}, 100);
});
$scope.updateCurrentWallet = function() {
walletService.getStatus($scope.wallet, {}, function(err, status) {
if (err) {
$log.error(err);
}
$timeout(function() {
$scope.wallet = profileService.getWallet($scope.wallet.id);
$scope.wallet.status = status;
$scope.setAddress();
$scope.$apply();
}, 200);
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.wallets = profileService.getWallets();
lodash.each($scope.wallets, function(wallet, index) {
$scope.loadAddresses(wallet);
});
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId, type, n) {
// Update current address
if ($scope.wallet && walletId == $scope.wallet.id) $scope.updateCurrentWallet();
})
];
// Update current wallet
if ($scope.wallet) $scope.updateCurrentWallet();
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
});

View file

@ -49,7 +49,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var updateStatus = function(force) {
$scope.updatingStatus = true;
$scope.updateStatusError = false;
$scope.updateStatusError = null;
$scope.walletNotRegistered = false;
walletService.getStatus($scope.wallet, {
@ -60,18 +60,16 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (err === 'WALLET_NOT_REGISTERED') {
$scope.walletNotRegistered = true;
} else {
$scope.updateStatusError = true;
$scope.updateStatusError = err;
}
$scope.status = null;
return;
} else {
setPendingTxps(status.pendingTxps);
$scope.status = status;
}
setPendingTxps(status.pendingTxps);
$scope.status = status;
$timeout(function() {
$scope.$apply();
}, 1);
});
});
};
@ -114,6 +112,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (err) return;
$timeout(function() {
walletService.startScan($scope.wallet, function() {
$scope.updateAll();
$scope.$apply();
});
});

View file

@ -739,30 +739,30 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
abstract: true,
template: '<ion-nav-view name="feedback"></ion-nav-view>'
})
.state('feedback.sendFeedback', {
url: '/sendFeedback/:score',
.state('feedback.send', {
url: '/send/:score',
views: {
'feedback': {
controller: 'sendFeedbackController',
templateUrl: 'views/feedback/sendFeedback.html'
controller: 'sendController',
templateUrl: 'views/feedback/send.html'
}
}
})
.state('feedback.thanks', {
url: '/thanks/:score/:skipped',
.state('feedback.complete', {
url: '/complete/:score/:skipped',
views: {
'feedback': {
controller: 'thanksController',
templateUrl: 'views/feedback/thanks.html'
controller: 'completeController',
templateUrl: 'views/feedback/complete.html'
}
}
})
.state('feedback.rateAppStore', {
url: '/rateAppStore/:score',
.state('feedback.rateApp', {
url: '/rateApp/:score',
views: {
'feedback': {
controller: 'rateAppStoreController',
templateUrl: 'views/feedback/rateAppStore.html'
controller: 'rateAppController',
templateUrl: 'views/feedback/rateApp.html'
}
}
})
@ -1009,41 +1009,50 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
});
}
$log.info('Init profile...');
// Try to open local profile
profileService.loadAndBindProfile(function(err) {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$log.info('Verifying storage...');
storageService.verify(function(err) {
if (err) {
if (err.message && err.message.match('NOPROFILE')) {
$log.debug('No profile... redirecting');
$state.go('onboarding.welcome');
} else if (err.message && err.message.match('NONAGREEDDISCLAIMER')) {
if (lodash.isEmpty(profileService.getWallets())) {
$log.debug('No wallets and no disclaimer... redirecting');
$state.go('onboarding.welcome');
} else {
$log.debug('Display disclaimer... redirecting');
$state.go('onboarding.disclaimer', {
resume: true
});
}
} else {
throw new Error(err); // TODO
}
$log.error('Storage failed to verify: ' + err);
// TODO - what next?
} else {
profileService.storeProfileIfDirty();
$log.debug('Profile loaded ... Starting UX.');
scannerService.gentleInitialize();
$state.go('tabs.home');
$log.info('Storage OK');
}
// After everything have been loaded, initialize handler URL
$timeout(function() {
openURLService.init();
}, 1000);
$log.info('Init profile...');
// Try to open local profile
profileService.loadAndBindProfile(function(err) {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
if (err) {
if (err.message && err.message.match('NOPROFILE')) {
$log.debug('No profile... redirecting');
$state.go('onboarding.welcome');
} else if (err.message && err.message.match('NONAGREEDDISCLAIMER')) {
if (lodash.isEmpty(profileService.getWallets())) {
$log.debug('No wallets and no disclaimer... redirecting');
$state.go('onboarding.welcome');
} else {
$log.debug('Display disclaimer... redirecting');
$state.go('onboarding.disclaimer', {
resume: true
});
}
} else {
throw new Error(err); // TODO
}
} else {
profileService.storeProfileIfDirty();
$log.debug('Profile loaded ... Starting UX.');
scannerService.gentleInitialize();
$state.go('tabs.home');
}
// After everything have been loaded, initialize handler URL
$timeout(function() {
openURLService.init();
}, 1000);
});
});
});

View file

@ -174,7 +174,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data.cards, {id : cardId});
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Not card found'));
// Get invoices
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
@ -211,7 +211,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
if (err) return cb(err);
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data.cards, {id : cardId});
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Not card found'));
$http(_post('/api/v2/' + card.token, json, credentials)).then(function(data) {
$log.info('BitPay TopUp: SUCCESS');
@ -288,13 +288,11 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
});
};
root.remove = function(cb) {
storageService.removeBitpayCardCredentials(BITPAY_CARD_NETWORK, function(err) {
storageService.removeBitpayDebitCards(BITPAY_CARD_NETWORK, function(err) {
storageService.removeBitpayDebitCardsHistory(BITPAY_CARD_NETWORK, function(err) {
$log.info('BitPay Debit Cards Removed: SUCCESS');
return cb();
});
root.remove = function(card, cb) {
storageService.removeBitpayDebitCard(BITPAY_CARD_NETWORK, card, function(err) {
storageService.removeBitpayDebitCardHistory(BITPAY_CARD_NETWORK, card, function(err) {
$log.info('BitPay Debit Card(s) Removed: SUCCESS');
return cb();
});
});
};

View file

@ -24,5 +24,35 @@ angular.module('copayApp.services').factory('feedbackService', function($http, $
};
};
root.isVersionUpdated = function(currentVersion, savedVersion) {
if (!verifyTagFormat(currentVersion))
return 'Cannot verify the format of version tag: ' + currentVersion;
if (!verifyTagFormat(savedVersion))
return 'Cannot verify the format of the saved version tag: ' + savedVersion;
var current = formatTagNumber(currentVersion);
var saved = formatTagNumber(savedVersion);
if (saved.major > current.major || (saved.major == current.major && saved.minor > current.minor))
return false;
return true;
function verifyTagFormat(tag) {
var regex = /^v?\d+\.\d+\.\d+$/i;
return regex.exec(tag);
};
function formatTagNumber(tag) {
var formattedNumber = tag.replace(/^v/i, '').split('.');
return {
major: +formattedNumber[0],
minor: +formattedNumber[1],
patch: +formattedNumber[2]
};
};
};
return root;
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('storageService', function(logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo) {
.factory('storageService', function(logHeader, fileStorageService, localStorageService, sjcl, $log, lodash, platformInfo, $timeout) {
var root = {};
@ -74,7 +74,90 @@ angular.module('copayApp.services')
});
};
////////////////////////////////////////////////////////////////////////////
//
// UPGRADING STORAGE
//
// 1. Write a function to upgrade the desired storage key(s). The function should have the protocol:
//
// _upgrade_x(key, network, cb), where:
//
// `x` is the name of the storage key
// `key` is the name of the storage key being upgraded
// `network` is one of 'livenet', 'testnet'
//
// 2. Add the storage key to `_upgraders` object using the name of the key as the `_upgrader` object key
// with the value being the name of the upgrade function (e.g., _upgrade_x). In order to avoid conflicts
// when a storage key is involved in multiple upgraders as well as predicte the order in which upgrades
// occur the `_upgrader` object key should be prefixed with '##_' (e.g., '01_') to create a unique and
// sortable name. This format is interpreted by the _upgrade() function.
//
// Upgraders are executed in numerical order per the '##_' object key prefix.
//
var _upgraders = {
'00_bitpayDebitCards' : _upgrade_bitpayDebitCards // 2016-11: Upgrade bitpayDebitCards-x to bitpayAccounts-x
};
function _upgrade_bitpayDebitCards(key, network, cb) {
key += '-' + network;
storage.get(key, function(err, data) {
if (err) return cb(err);
if (data != null) {
// Needs upgrade
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
root.setBitpayDebitCards(network, data, function(err) {
if (err) return cb(err);
storage.remove(key, function() {
cb(null, 'replaced with \'bitpayAccounts\'');
});
});
} else {
cb();
}
});
};
//
////////////////////////////////////////////////////////////////////////////
// IMPORTANT: This function is designed to block execution until it completes.
// Ideally storage should not be used until it has been verified.
root.verify = function(cb) {
_upgrade(function(err) {
cb(err);
});
};
function _handleUpgradeError(key, err) {
$log.error('Failed to upgrade storage for \'' + key + '\': ' + err);
};
function _handleUpgradeSuccess(key, msg) {
$log.info('Storage upgraded for \'' + key + '\': ' + msg);
};
function _upgrade(cb) {
var errorCount = 0;
var errorMessage = undefined;
var keys = Object.keys(_upgraders).sort();
var networks = ['livenet', 'testnet'];
keys.forEach(function(key) {
networks.forEach(function(network) {
var storagekey = key.split('_')[1];
_upgraders[key](storagekey, network, function(err, msg) {
if (err) {
_handleUpgradeError(storagekey, err);
errorCount++;
errorMessage = errorCount + ' storage upgrade failures';
}
if (msg) _handleUpgradeSuccess(storagekey, msg);
});
});
});
cb(errorMessage);
};
root.tryToMigrate = function(cb) {
if (!shouldUseFileStorage) return cb();
@ -138,12 +221,12 @@ angular.module('copayApp.services')
storage.remove('profile', cb);
};
root.setProfileCreationTime = function(time, cb) {
storage.set('profileCreationTime', time, cb);
root.setFeedbackInfo = function(feedbackValues, cb) {
storage.set('feedback', feedbackValues, cb);
};
root.getProfileCreationTime = function(cb) {
storage.get('profileCreationTime', cb);
root.getFeedbackInfo = function(cb) {
storage.get('feedback', cb);
};
root.storeFocusedWalletId = function(id, cb) {
@ -211,14 +294,6 @@ angular.module('copayApp.services')
storage.set('homeTip', val, cb);
};
root.getRateCardFlag = function(cb) {
storage.get('rateCardFlag', cb);
};
root.setRateCardFlag = function(val, cb) {
storage.set('rateCardFlag', val, cb);
};
root.setHideBalanceFlag = function(walletId, val, cb) {
storage.set('hideBalance-' + walletId, val, cb);
};
@ -349,20 +424,76 @@ angular.module('copayApp.services')
storage.get('bitpayDebitCardsHistory-' + network, cb);
};
root.removeBitpayDebitCardsHistory = function(network, cb) {
storage.remove('bitpayDebitCardsHistory-' + network, cb);
root.removeBitpayDebitCardHistory = function(network, card, cb) {
root.getBitpayDebitCardsHistory(network, function(err, data) {
if (err) return cb(err);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
delete data[card.eid];
root.setBitpayDebitCardsHistory(network, JSON.stringify(data), cb);
});
};
root.setBitpayDebitCards = function(network, data, cb) {
storage.set('bitpayDebitCards-' + network, data, cb);
if (lodash.isString(data)) {
data = JSON.parse(data);
}
data = data || {};
if (lodash.isEmpty(data) || !data.email) return cb('No card(s) to set');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
if (err) return cb(err);
bitpayAccounts = JSON.parse(bitpayAccounts) || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data;
storage.set('bitpayAccounts-' + network, JSON.stringify(bitpayAccounts), cb);
});
};
root.getBitpayDebitCards = function(network, cb) {
storage.get('bitpayDebitCards-' + network, cb);
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
bitpayAccounts = JSON.parse(bitpayAccounts) || {};
var cards = [];
Object.keys(bitpayAccounts).forEach(function(email) {
// For the UI, add the account email to the card object.
var acctCards = bitpayAccounts[email]['bitpayDebitCards-' + network].cards;
for (var i = 0; i < acctCards.length; i++) {
acctCards[i].email = email;
}
cards = cards.concat(acctCards);
});
cb(err, cards);
});
};
root.removeBitpayDebitCards = function(network, cb) {
storage.remove('bitpayDebitCards-' + network, cb);
root.removeBitpayDebitCard = function(network, card, cb) {
if (lodash.isString(card)) {
card = JSON.parse(card);
}
card = card || {};
if (lodash.isEmpty(card) || !card.eid) return cb('No card to remove');
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
if (err) cb(err);
bitpayAccounts = JSON.parse(bitpayAccounts) || {};
Object.keys(bitpayAccounts).forEach(function(userId) {
var data = bitpayAccounts[userId]['bitpayDebitCards-' + network];
var newCards = lodash.reject(data.cards, {'eid': card.eid});
data.cards = newCards;
root.setBitpayDebitCards(network, data, function(err) {
if (err) cb(err);
// If there are no more cards in storage then re-enable the next step entry.
root.getBitpayDebitCards(network, function(err, cards){
if (err) cb(err);
if (cards.length == 0) {
root.removeNextStep('BitpayCard', cb);
} else {
cb();
}
});
});
});
});
};
root.setBitpayCardCredentials = function(network, data, cb) {

View file

@ -29,16 +29,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
// // RECEIVE
// // Check address
// root.isUsed(wallet.walletId, balance.byAddress, function(err, used) {
// if (used) {
// $log.debug('Address used. Creating new');
// $rootScope.$emit('Local/AddressIsUsed');
// }
// });
//
var _signWithLedger = function(wallet, txp, cb) {
$log.info('Requesting Ledger Chrome app to sign the transaction');
@ -67,32 +57,6 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
// TODO
// This handles errors from BWS/index which normally
// trigger from async events (like updates).
// Debounce function avoids multiple popups
var _handleError = function(err) {
$log.warn('wallet ERROR: ', err);
$log.warn('TODO');
return; // TODO!!!
if (err instanceof errors.NOT_AUTHORIZED) {
console.log('[walletService.js.93] TODO NOT AUTH'); //TODO
// TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
wallet.notAuthorized = true;
$state.go('tabs.home');
} else if (err instanceof errors.NOT_FOUND) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not access Wallet Service: Not found'));
} else {
var msg = ""
$rootScope.$emit('Local/ClientError', (err.error ? err.error : err));
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, gettextCatalog.getString('Error at Wallet Service')));
}
};
root.handleError = lodash.debounce(_handleError, 1000);
root.invalidateCache = function(wallet) {
if (wallet.cachedStatus)
wallet.cachedStatus.isValid = false;
@ -220,7 +184,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
cache.totalBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat) + ' ' + cache.unitName;
cache.lockedBalanceStr = txFormatService.formatAmount(cache.lockedBalanceSat) + ' ' + cache.unitName;
cache.availableBalanceStr = txFormatService.formatAmount(cache.availableBalanceSat) + ' ' + cache.unitName;
cache.pendingBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat + (cache.pendingAmount === null? 0 : cache.pendingAmount)) + ' ' + cache.unitName;
cache.pendingBalanceStr = txFormatService.formatAmount(cache.totalBalanceSat + (cache.pendingAmount === null ? 0 : cache.pendingAmount)) + ' ' + cache.unitName;
if (cache.pendingAmount !== null && cache.pendingAmount !== 0) {
cache.pendingAmountStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + cache.unitName;
@ -231,6 +195,17 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
cache.alternativeName = config.settings.alternativeName;
cache.alternativeIsoCode = config.settings.alternativeIsoCode;
// Check address
root.isAddressUsed(wallet, balance.byAddress, function(err, used) {
if (used) {
$log.debug('Address used. Creating new');
// Force new address
root.getAddress(wallet, true, function(err, addr) {
$log.debug('New address: ', addr);
});
}
});
rateService.whenAvailable(function() {
var totalBalanceAlternative = rateService.toFiat(cache.totalBalanceSat, cache.alternativeIsoCode);
@ -760,12 +735,13 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.isUsed = function(wallet, byAddress, cb) {
// Check address
root.isAddressUsed = function(wallet, byAddress, cb) {
storageService.getLastAddress(wallet.id, function(err, addr) {
var used = lodash.find(byAddress, {
address: addr
});
return cb(null, used);
return cb(err, used);
});
};
@ -798,12 +774,14 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.getAddress = function(wallet, forceNew, cb) {
storageService.getLastAddress(wallet.id, function(err, addr) {
if (err) return cb(err);
if (!forceNew && addr) return cb(null, addr);
if (!wallet.isComplete())
return cb('WALLET_NOT_COMPLETE');
createAddress(wallet, function(err, _addr) {
if (err) return cb(err, addr);
storageService.storeLastAddress(wallet.id, _addr, function() {

View file

@ -0,0 +1,15 @@
#bitpayCardPreferences {
.item {
.item-title {
display: block;
}
.item-subtitle {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: $light-gray;
font-size: 14px;
}
}
}

View file

@ -1,4 +1,4 @@
#thanks-feedback {
#complete {
background-color: #ffffff;
.item-heading {
border-style: none;
@ -19,10 +19,22 @@
}
.subtitle {
padding: 10px 30px 20px 40px;
.image {
text-align: center;
padding: 30px;
}
}
.icon-svg > img {
height: 16rem;
width: 16rem;
margin: 10px;
}
.socialsharing-icon {
display: inline-block;
width: 50px;
height: 50px;
border-radius: 50%;
}
.addressbook-icon-svg {
display: inline-block;
width: 50px;
height: 50px;
}
.share-buttons {
bottom: 0;

View file

@ -1,14 +1,19 @@
#rate-app-store {
#rate-app {
background-color: #ffffff;
.skip {
margin: 10px;
color: #667;
}
.icon-svg > img {
width: 80px;
height: 80px;
margin-top: 15px;
}
.title {
font-size: 20px;
font-weight: bold;
color: $dark-gray;
margin: 40px 10px;
margin: 40px 50px 10px;
text-align: center;
}
.subtitle {

View file

@ -1,20 +1,12 @@
#rate-card {
.stars {
display: flex;
border-bottom: none;
.button {
background-color: #fff;
outline: none;
border: none;
height:10px;
width:10px;
}
.gold {
color: #ffd700;
}
.subtle-gray {
color: $subtle-gray;
}
.row {
border: none;
}
.row.row-margin{
margin: 20px 0px 20px 0px;
}
.item-icon-right {
margin: 0;
}
.feedback-flow-button {
padding: 20px;

View file

@ -1,5 +1,8 @@
#send-feedback {
background-color: #ffffff;
.row {
border: none;
}
.skip {
margin: 20px 20px 10px;
color: #667;
@ -11,18 +14,6 @@
font-weight: bold;
color: $dark-gray;
}
.star {
a {
font-size: 25px;
padding: 20px;
.gold {
color: #ffd700;
}
.subtle-gray {
color: $subtle-gray;
}
}
}
.comment {
padding: 20px;
font-size: 1rem;

View file

@ -9,6 +9,7 @@
box-shadow:$subtle-box-shadow;
padding:0;
border-radius: 6px;
margin: 0 auto;
@media (min-width: 500px) {
& {
width: 350px;
@ -32,7 +33,7 @@
visibility: hidden;
}
&.visible{
visibility: visible !important;
visibility: visible !important;
}
}
.big-icon-svg{
@ -64,10 +65,10 @@
transform: scale(.8);
}
&.swiper-slide-prev {
left: 10vw;
left: 5vw;
}
&.swiper-slide-next {
left: -10vw;
left: -5vw;
}
}
}

View file

@ -49,6 +49,7 @@
%cta-buttons {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}

View file

@ -13,7 +13,7 @@
.scroll{height:100%;}
#address {
background: #fff;
height: calc(100vh - 33vh);
height: calc(100vh - 34vh);
display: flex;
align-items: center;
justify-content: center;
@ -22,14 +22,26 @@
@media(max-height: 600px){
height: calc(100vh - 36vh);
}
&-info{
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
}
article{
flex:1;
width: 100%;
}
#bit-address{
position: absolute;
bottom:0;
width:100%;
width: 100%;
align-self: flex-end;
margin-top: auto;
justify-content: center;
align-content: center;
position: relative;
min-height: 40px;
#next-address{
color:$light-gray;
}
@ -58,6 +70,31 @@
}
.bit-address {
font-size: .8rem;
// left:10%;
position: absolute;
transition: all .4s ease;
width:100%;
height: 100%;
z-index: 0;
position: absolute;
top:50%;
&, &.next{
left:100%;
transform: translate(100%, -40%);
}
&.next, &.prev{
z-index:2;
}
&.current, &.prev{
left:50%;
}
&.current{
transform: translate(-50%, -40%);
z-index: 3;
}
&.prev{
transform: translate(-150%, -40%);
}
.item {
padding-top: 5px;
padding-bottom: 5px;
@ -67,6 +104,32 @@
}
.qr {
padding: calc(100vh - 85vh) 0 calc(100vh - 96vh);
align-self: center;
margin-top: auto;
height: 220px;
position: relative;
justify-content: center;
flex: 1;
div{
transition: all .4s ease;
&.current, &.prev, &.next{
position: absolute;
top:50%;
}
&.current, &.prev{
left:50%;
}
&.current{
transform: translate(-50%, -40%);
}
&.prev{
transform: translate(-150%, -40%);
}
&.next{
left:100%;
transform: translate(100%, -40%);
}
}
@media(max-height: 700px){
padding: calc(100vh - 90vh) 0 calc(100vh - 96vh);
}
@ -74,13 +137,13 @@
display: flex;
justify-content: center;
align-items: center;
position: relative;
min-height: 220px;
}
}
#qr-options{
display: flex;
flex-direction: row;
justify-content: center;
align-content: center;
align-self: flex-end;
.item{
i{left:25px;}
}
@ -90,6 +153,8 @@
color: #fff;
position: absolute;
top: 0;
left:0;
z-index: 2;
i {
padding: 10px;
}
@ -122,7 +187,7 @@
left: 50%;
-webkit-transform: translateX(-50%);
transform: translateX(-50%);
z-index: 2;
z-index: 4;
}
}
#first-time-tip {
@ -165,6 +230,9 @@
border-right: 1px solid rgb(228, 228, 228);
padding-right: 10px;
}
#wallets{
#sidebar-wallet{display: none;}
}
.wallets{
position: relative;
height: calc(100vh - 83vh);
@ -175,6 +243,17 @@
top: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
max-width: 450px;
.swiper-slide{
text-align: center;
.card{
margin-top:2vh;
display: inline-block;
width:80%;
}
&-next{left:-25%;}
&-prev{left:25%;}
}
@media (max-height: 600px){
&{
-webkit-transform: translate(-50%, -58%);
@ -184,23 +263,107 @@
}
}
}
// @media(min-width: 700px) and (min-height: 700px){
// .wallets{display: none;}
// #address{
// height:90vh;
// width:75%;
// .qr{
// height: 70%;
// div{
// transform: scale(1.5);
// }
// }
// #bit-address{
// height: 10%;
// padding: calc(100vh - 99vh);
// }
// }
// }
@media(min-width: 700px) and (min-height: 700px){
.wallets{display: none;}
#address{
float:left;
height:90vh;
width:65%;
&-info{
height: 100%;
}
.qr{
height: 70%;
div{
opacity: 0;
transition: none;
&.current{
opacity: 1;
}
}
}
.bit-address{
opacity: 0;
transition: none;
&.current{
opacity: 1;
}
}
.backup, #bit-address{left:0;}
#bit-address{
height: 10%;
padding: calc(100vh - 99vh);
}
}
#wallets{
float:left;
width:35%;
height: 100%;
display: flex;
flex-direction: column;
overflow: visible;
#sidebar-wallet{display: block;}
.list{height: 100%;overflow: visible;}
#wallet-list{
position: absolute;
width: 100%;
overflow-y: scroll;
height: 100%;
left: -6%;
}
.wallet{
position: relative;
&.current{
&:before {
right: 93%;
top: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
border-right-color: #f2f2f2;
border-width: 20px;
margin-top: -20px;
}
}
}
.card {
max-width: 350px;
box-shadow:$subtle-box-shadow;
padding:0;
border-radius: 6px;
padding:2px;
width: 80%;
position: relative;
margin: 1.5rem auto 0;
.item{
padding: 6% 10% 6% 8%;
i{left:auto;}
span{
clear:both;
width: 100%;
display: inline-block;
&.wallet-name{
margin-top:10px;
margin-bottom:5px;
font-size:13px;
}
&.wallet-number{
visibility: hidden;
}
&.visible{
visibility: visible !important;
}
}
.big-icon-svg{
& > .bg{padding:.3rem;width: 40px;height:40px;}
}
}
}
}
}
}
@keyframes fadeIn {

View file

@ -13,14 +13,15 @@
@import "advancedSettings";
@import "bitpayCard";
@import "bitpayCardIntro";
@import "bitpayCardPreferences";
@import "address-book";
@import "wallet-backup-phrase";
@import "zero-state";
@import "onboarding/onboarding";
@import "feedback/rateCard";
@import "feedback/sendFeedback";
@import "feedback/thanks";
@import "feedback/rateAppStore";
@import "feedback/send";
@import "feedback/complete";
@import "feedback/rateApp";
@import "includes/actionSheet";
@import "export";
@import "import";