Merge branch 'master' of https://github.com/bitpay/copay into feat/app-identity

This commit is contained in:
Andy Phillipson 2016-12-22 09:59:35 -05:00
commit 9b3a3aab9d
213 changed files with 12663 additions and 7021 deletions

View file

@ -26,7 +26,10 @@ angular.module('copayApp.controllers').controller('activityController',
$scope.openNotificationModal = function(n) {
if (n.txid) {
openTxModal(n);
$state.transitionTo('tabs.wallet.tx-details', {
txid: n.txid,
walletId: n.walletId
});
} else {
var txp = lodash.find($scope.txps, {
id: n.txpId
@ -46,35 +49,4 @@ angular.module('copayApp.controllers').controller('activityController',
}
}
};
var openTxModal = function(n) {
var wallet = profileService.getWallet(n.walletId);
ongoingProcess.set('loadingTxInfo', true);
walletService.getTx(wallet, n.txid, function(err, tx) {
ongoingProcess.set('loadingTxInfo', false);
if (err) {
$log.error(err);
return popupService.showAlert(gettextCatalog.getString('Error'), err);
}
if (!tx) {
$log.warn('No tx found');
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
}
$scope.wallet = wallet;
$scope.btx = lodash.cloneDeep(tx);
$state.transitionTo('tabs.wallet.tx-details', {
txid: $scope.btx.txid,
walletId: $scope.walletId
});
walletService.getTxNote(wallet, n.txid, function(err, note) {
if (err) $log.warn('Could not fetch transaction note: ' + err);
$scope.btx.note = note;
});
});
};
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('addressbookListController', function($scope, $log, $timeout, addressbookService, lodash, popupService) {
angular.module('copayApp.controllers').controller('addressbookListController', function($scope, $log, $timeout, addressbookService, lodash, popupService, gettextCatalog) {
var contacts;

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('addressbookAddController', function($scope, $state, $stateParams, $timeout, $ionicHistory, addressbookService, popupService) {
angular.module('copayApp.controllers').controller('addressbookAddController', function($scope, $state, $stateParams, $timeout, $ionicHistory, gettextCatalog, addressbookService, popupService) {
$scope.fromSendTab = $stateParams.fromSendTab;

View file

@ -0,0 +1,199 @@
'use strict';
angular.module('copayApp.controllers').controller('addressesController', function($scope, $stateParams, $state, $timeout, $ionicHistory, $ionicPopover, $ionicScrollDelegate, configService, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, platformInfo) {
var UNUSED_ADDRESS_LIMIT = 5;
var BALANCE_ADDRESS_LIMIT = 5;
var MENU_ITEM_HEIGHT = 55;
var config;
var unitName;
var unitToSatoshi;
var satToUnit;
var unitDecimals;
var withBalance;
$scope.showInfo = false;
$scope.showMore = false;
$scope.allAddressesView = false;
$scope.isCordova = platformInfo.isCordova;
$scope.wallet = profileService.getWallet($stateParams.walletId);
function init() {
ongoingProcess.set('gettingAddresses', true);
walletService.getMainAddresses($scope.wallet, {}, function(err, addresses) {
if (err) {
ongoingProcess.set('gettingAddresses', false);
return popupService.showAlert(gettextCatalog.getString('Error'), err);
}
var allAddresses = addresses;
walletService.getBalance($scope.wallet, {}, function(err, resp) {
ongoingProcess.set('gettingAddresses', false);
if (err) {
return popupService.showAlert(gettextCatalog.getString('Error'), err);
}
withBalance = resp.byAddress;
var idx = lodash.indexBy(withBalance, 'address');
$scope.noBalance = lodash.reject(allAddresses, function(x) {
return idx[x.address];
});
processPaths($scope.noBalance);
processPaths(withBalance);
$scope.latestUnused = lodash.slice($scope.noBalance, 0, UNUSED_ADDRESS_LIMIT);
$scope.latestWithBalance = lodash.slice(withBalance, 0, BALANCE_ADDRESS_LIMIT);
lodash.each(withBalance, function(a) {
a.balanceStr = (a.amount * satToUnit).toFixed(unitDecimals) + ' ' + unitName;
});
$scope.viewAll = {
value: $scope.noBalance.length > UNUSED_ADDRESS_LIMIT || withBalance.length > BALANCE_ADDRESS_LIMIT
};
$scope.allAddresses = $scope.noBalance.concat(withBalance);
$scope.$digest();
});
});
};
function processPaths(list) {
lodash.each(list, function(n) {
n.path = n.path.replace(/^m/g, 'xpub');
});
};
$scope.newAddress = function() {
if ($scope.gapReached) return;
ongoingProcess.set('generatingNewAddress', true);
walletService.getAddress($scope.wallet, true, function(err, addr) {
if (err) {
ongoingProcess.set('generatingNewAddress', false);
$scope.gapReached = true;
$timeout(function() {
$scope.$digest();
});
return;
}
walletService.getMainAddresses($scope.wallet, {
limit: 1
}, function(err, _addr) {
ongoingProcess.set('generatingNewAddress', false);
if (err) return popupService.showAlert(gettextCatalog.getString('Error'), err);
if (addr != _addr[0].address) return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('New address could not be generated. Please try again.'));
$scope.noBalance = [_addr[0]].concat($scope.noBalance);
$scope.latestUnused = lodash.slice($scope.noBalance, 0, UNUSED_ADDRESS_LIMIT);
$scope.viewAll = {
value: $scope.noBalance.length > UNUSED_ADDRESS_LIMIT
};
$scope.$digest();
});
});
};
$scope.viewAllAddresses = function() {
$state.go('tabs.receive.allAddresses', {
walletId: $scope.wallet.id
});
};
$scope.showInformation = function() {
$timeout(function() {
$scope.showInfo = !$scope.showInfo;
$ionicScrollDelegate.resize();
}, 10);
};
$scope.readMore = function() {
$timeout(function() {
$scope.showMore = !$scope.showMore;
$ionicScrollDelegate.resize();
}, 10);
};
$scope.showMenu = function(allAddresses, $event) {
var scanObj = {
text: gettextCatalog.getString('Scan addresses for funds'),
action: scan,
};
var sendAddressesObj = {
text: gettextCatalog.getString('Send addresses by email'),
action: sendByEmail,
}
$scope.items = allAddresses ? [sendAddressesObj] : [scanObj];
$scope.height = $scope.items.length * MENU_ITEM_HEIGHT;
$ionicPopover.fromTemplateUrl('views/includes/menu-popover.html', {
scope: $scope
}).then(function(popover) {
$scope.menu = popover;
$scope.menu.show($event);
});
};
var scan = function() {
walletService.startScan($scope.wallet);
$scope.menu.hide();
$ionicHistory.clearHistory();
$state.go('tabs.home');
};
var sendByEmail = function() {
function formatDate(ts) {
var dateObj = new Date(ts * 1000);
if (!dateObj) {
$log.debug('Error formating a date');
return 'DateError';
}
if (!dateObj.toJSON()) {
return '';
}
return dateObj.toJSON();
};
ongoingProcess.set('sendingByEmail', true);
$timeout(function() {
var body = 'Copay Wallet "' + $scope.walletName + '" Addresses\n Only Main Addresses are shown.\n\n';
body += "\n";
body += $scope.allAddresses.map(function(v) {
return ('* ' + v.address + ' ' + 'xpub' + v.path.substring(1) + ' ' + formatDate(v.createdOn));
}).join("\n");
ongoingProcess.set('sendingByEmail', false);
window.plugins.socialsharing.shareViaEmail(
body,
'Copay Addresses',
null, // TO: must be null or an array
null, // CC: must be null or an array
null, // BCC: must be null or an array
null, // FILES: can be null, a string, or an array
function() {},
function() {}
);
$scope.menu.hide();
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.allAddressesView = data.stateName == 'tabs.receive.allAddresses' ? true : false;
$timeout(function() {
$scope.$apply();
});
});
$scope.$on("$ionicView.afterEnter", function(event, data) {
config = configService.getSync().wallet.settings;
unitToSatoshi = config.unitToSatoshi;
satToUnit = 1 / unitToSatoshi;
unitName = config.unitName;
unitDecimals = config.unitDecimals;
if (!$scope.allAddresses || $scope.allAddresses.length < 0) init();
});
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $rootScope, $log, $window, lodash, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService) {
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $rootScope, $log, $window, lodash, configService, uxLanguage, platformInfo, pushNotificationsService, profileService, feeService, storageService, $ionicHistory, $timeout, $ionicScrollDelegate) {
var updateConfig = function() {
@ -29,6 +29,50 @@ angular.module('copayApp.controllers').controller('advancedSettingsController',
};
};
$scope.global = $rootScope;
if (!$scope.global.developmentUtilitiesEnabled) {
$scope.global.developmentUtilitiesEnabled = {
value: false
};
}
$scope.toggledDevelopmentUtils = function() {
if ($scope.global.developmentUtilitiesEnabled.value) {
$log.debug('User enabled development utilities.');
$timeout(function() {
$ionicScrollDelegate.resize();
}, 10);
} else {
$log.debug('User disabled development utilities.');
}
}
$scope.activateFeedbackCard = function() {
$scope.feedbackCardActivating = true;
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = JSON.parse(info);
// hardcoding so we can distinguish from normal operation
feedbackInfo.time = 1231006505; // genesis block time
feedbackInfo.version = window.version;
feedbackInfo.sent = false;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {
$log.debug('Activated feedback card with: ' + JSON.stringify(feedbackInfo));
$ionicHistory.clearCache();
$timeout(function() {
$scope.feedbackCardActivating = false;
$scope.feedbackCardActivated = true;
$timeout(function() {
$scope.feedbackCardActivated = false;
}, 10000);
}, 500);
});
});
}
$scope.resetActivateFeedbackCard = function() {
$scope.feedbackCardActivated = false;
}
$scope.spendUnconfirmedChange = function() {
var opts = {
wallet: {

View file

@ -1,12 +1,12 @@
'use strict';
angular.module('copayApp.controllers').controller('amazonController',
function($scope, $timeout, $ionicModal, $log, lodash, bwcError, amazonService, platformInfo, externalLinkService, popupService) {
function($scope, $timeout, $ionicModal, $log, lodash, amazonService, platformInfo, externalLinkService, popupService, gettextCatalog) {
$scope.network = amazonService.getEnvironment();
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var initAmazon = function() {
@ -19,6 +19,16 @@ angular.module('copayApp.controllers').controller('amazonController',
$timeout(function() {
$scope.$digest();
});
if ($scope.cardClaimCode) {
var card = lodash.find($scope.giftCards, {
claimCode: $scope.cardClaimCode
});
if (lodash.isEmpty(card)) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Card not found'));
return;
}
$scope.openCardModal(card);
}
});
$scope.updatePendingGiftCards();
};
@ -26,12 +36,16 @@ angular.module('copayApp.controllers').controller('amazonController',
$scope.updatePendingGiftCards = lodash.debounce(function() {
amazonService.getPendingGiftCards(function(err, gcds) {
$timeout(function() {
$scope.giftCards = gcds;
$scope.$digest();
});
lodash.forEach(gcds, function(dataFromStorage) {
if (dataFromStorage.status == 'PENDING') {
$log.debug("creating gift card");
amazonService.createGiftCard(dataFromStorage, function(err, giftCard) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
if (giftCard.status != 'PENDING') {
@ -84,6 +98,7 @@ angular.module('copayApp.controllers').controller('amazonController',
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.cardClaimCode = data.stateParams.cardClaimCode;
initAmazon();
});
});

View file

@ -1,34 +1,51 @@
'use strict';
angular.module('copayApp.controllers').controller('amountController', function($rootScope, $scope, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService) {
angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicScrollDelegate, $ionicHistory, $ionicPopover, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, bitpayCardService, popupService, bwcError, payproService, profileService, bitcore, amazonService, glideraService) {
var unitToSatoshi;
var satToUnit;
var unitDecimals;
var satToBtc;
var self = $scope.self;
var SMALL_FONT_SIZE_LIMIT = 10;
var LENGTH_EXPRESSION_LIMIT = 19;
var MENU_ITEM_HEIGHT = 55;
$scope.$on('$ionicView.leave', function() {
angular.element($window).off('keydown');
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isGiftCard = data.stateParams.isGiftCard;
// Glidera parameters
$scope.isGlidera = data.stateParams.isGlidera;
$scope.glideraAccessToken = data.stateParams.glideraAccessToken;
$scope.isWallet = data.stateParams.isWallet;
$scope.cardId = data.stateParams.cardId;
$scope.showMenu = $ionicHistory.backView().stateName == 'tabs.send';
var isWallet = data.stateParams.isWallet || 'false';
$scope.isWallet = (isWallet.toString().trim().toLowerCase() == 'true' ? true : false);
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.showAlternativeAmount = !!$scope.cardId;
$scope.showAlternativeAmount = !!$scope.cardId || !!$scope.isGiftCard || !!$scope.isGlidera;
$scope.toColor = data.stateParams.toColor;
if (!$scope.cardId && !$stateParams.toAddress) {
$scope.customAmount = data.stateParams.customAmount;
if (!$scope.cardId && !$scope.isGiftCard && !$scope.isGlidera && !data.stateParams.toAddress) {
$log.error('Bad params at amount')
throw ('bad params');
}
if ($scope.isGlidera) {
glideraService.getLimits($scope.glideraAccessToken, function(err, limits) {
$scope.limits = limits;
$timeout(function() {
$scope.$apply();
});
});
}
var reNr = /^[1234567890\.]$/;
var reOp = /^[\*\+\-\/]$/;
@ -49,8 +66,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
$timeout(function() {
$scope.$apply();
}, 10);
});
});
var config = configService.getSync().wallet.settings;
@ -77,6 +93,35 @@ angular.module('copayApp.controllers').controller('amountController', function($
}, 10);
});
$scope.showSendMaxMenu = function($event) {
var sendMaxObj = {
text: gettextCatalog.getString('Send max amount'),
action: setSendMax,
};
$scope.items = [sendMaxObj];
$scope.height = $scope.items.length * MENU_ITEM_HEIGHT;
$ionicPopover.fromTemplateUrl('views/includes/menu-popover.html', {
scope: $scope
}).then(function(popover) {
$scope.menu = popover;
$scope.menu.show($event);
});
};
function setSendMax() {
$scope.menu.hide();
$state.transitionTo('tabs.send.confirm', {
isWallet: $scope.isWallet,
toAmount: null,
toAddress: $scope.toAddress,
toName: $scope.toName,
toEmail: $scope.toEmail,
useSendMax: true,
});
};
$scope.toggleAlternative = function() {
$scope.showAlternativeAmount = !$scope.showAlternativeAmount;
@ -121,7 +166,6 @@ angular.module('copayApp.controllers').controller('amountController', function($
function isExpression(val) {
var regex = /^\.?\d+(\.?\d+)?([\/\-\+\*x]\d?\.?\d+)+$/;
return regex.test(val);
};
@ -134,7 +178,6 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.resetAmount = function() {
$scope.amount = $scope.alternativeResult = $scope.amountResult = $scope.globalResult = '';
$scope.allowSend = false;
checkFontSize();
};
@ -189,6 +232,20 @@ angular.module('copayApp.controllers').controller('amountController', function($
return result.replace('x', '*');
};
$scope.getRates = function() {
bitpayCardService.getRates($scope.alternativeIsoCode, function(err, res) {
if (err) {
$log.warn(err);
return;
}
if ($scope.unitName == 'bits') {
$scope.exchangeRate = '1,000,000 bits ~ ' + res.rate + ' ' + $scope.alternativeIsoCode;
} else {
$scope.exchangeRate = '1 BTC ~ ' + res.rate + ' ' + $scope.alternativeIsoCode;
}
});
};
$scope.finish = function() {
var _amount = evaluate(format($scope.amount));
@ -199,6 +256,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
amount: amountUSD,
currency: 'USD'
};
ongoingProcess.set('Preparing transaction...', true);
$timeout(function() {
@ -238,15 +296,87 @@ angular.module('copayApp.controllers').controller('amountController', function($
});
});
} else if ($scope.isGiftCard) {
ongoingProcess.set('Preparing transaction...', true);
// Get first wallet as UUID
var uuid;
try {
uuid = profileService.getWallets({
onlyComplete: true,
network: 'livenet',
})[0].id;
} catch (err) {
ongoingProcess.set('Preparing transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('No wallet found!'));
return;
};
var amountUSD = $scope.showAlternativeAmount ? _amount : $filter('formatFiatAmount')(toFiat(_amount));
var dataSrc = {
currency: 'USD',
amount: amountUSD,
uuid: uuid
};
amazonService.createBitPayInvoice(dataSrc, function(err, dataInvoice) {
if (err) {
ongoingProcess.set('Preparing transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
amazonService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) {
if (err) {
ongoingProcess.set('Preparing transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
var payProUrl = invoice.paymentUrls.BIP73;
payproService.getPayProDetails(payProUrl, function(err, payProDetails) {
ongoingProcess.set('Preparing transaction...', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
var stateParams = {
giftCardAmountUSD: amountUSD,
giftCardAccessKey: dataInvoice.accessKey,
giftCardInvoiceTime: invoice.invoiceTime,
giftCardUUID: dataSrc.uuid,
toAmount: payProDetails.amount,
toAddress: payProDetails.toAddress,
description: payProDetails.memo,
paypro: payProDetails
};
$state.transitionTo('tabs.giftcards.amazon.confirm', stateParams);
}, true);
});
});
} else if ($scope.isGlidera) {
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
$state.transitionTo('tabs.buyandsell.glidera.confirm', {
toAmount: (amount * unitToSatoshi).toFixed(0),
isGlidera: $scope.isGlidera,
glideraAccessToken: $scope.glideraAccessToken
});
} else {
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
$state.transitionTo('tabs.send.confirm', {
isWallet: $scope.isWallet,
toAmount: (amount * unitToSatoshi).toFixed(0),
toAddress: $scope.toAddress,
toName: $scope.toName,
toEmail: $scope.toEmail
});
if ($scope.customAmount) {
$state.transitionTo('tabs.receive.customAmount', {
toAmount: (amount * unitToSatoshi).toFixed(0),
toAddress: $scope.toAddress
});
} else {
$state.transitionTo('tabs.send.confirm', {
isWallet: $scope.isWallet,
toAmount: (amount * unitToSatoshi).toFixed(0),
toAddress: $scope.toAddress,
toName: $scope.toName,
toEmail: $scope.toEmail
});
}
}
};
});

View file

@ -72,7 +72,7 @@ angular.module('copayApp.controllers').controller('backupController',
var showBackupResult = function() {
if ($scope.backupError) {
var title = 'Uh oh...';
var title = gettextCatalog.getString('Uh oh...');
var message = gettextCatalog.getString("It's important that you write your backup phrase down correctly. If something happens to your wallet, you'll need this backup to recover your money. Please review your backup and try again.");
popupService.showAlert(title, message, function() {
$scope.setFlow(2);

View file

@ -63,7 +63,9 @@ angular.module('copayApp.controllers').controller('bitpayCardController', functi
if (err) {
$log.error(err);
$scope.error = gettextCatalog.getString('Could not get transactions');
self.bitpayCardTransactionHistory = null;
self.bitpayCardCurrentBalance = null;
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get transactions'));
return;
}

View file

@ -36,7 +36,7 @@ angular.module('copayApp.controllers').controller('bitpayCardIntroController', f
popupService.showConfirm(title, msg, ok, cancel, function(res) {
if (res) {
// Set flag for nextStep
storageService.setNextStep('BitpayCard', true, function(err) {});
storageService.setNextStep('BitpayCard', 'true', function(err) {});
// Save data
bitpayCardService.setBitpayDebitCards(data, function(err) {
if (err) return;

View file

@ -1,224 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('buyAmazonController',
function($scope, $log, $timeout, $state, lodash, profileService, bwcError, gettextCatalog, configService, walletService, amazonService, ongoingProcess, platformInfo, externalLinkService, popupService) {
var self = this;
var network = amazonService.getEnvironment();
var wallet;
$scope.$on('Wallet/Changed', function(event, w) {
if (lodash.isEmpty(w)) {
$log.debug('No wallet provided');
return;
}
wallet = w;
$log.debug('Wallet changed: ' + w.name);
});
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
this.confirm = function() {
var message = gettextCatalog.getString('Amazon.com Gift Card purchase for ${{amount}} USD', {
amount: $scope.formData.fiat
});
var ok = gettextCatalog.getString('Buy');
popupService.showConfirm(null, message, ok, null, function(res) {
if (res) self.createTx();
});
};
this.createTx = function() {
self.errorInfo = null;
if (lodash.isEmpty(wallet)) return;
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg('MISSING_PRIVATE_KEY'));
return;
}
var dataSrc = {
currency: 'USD',
amount: $scope.formData.fiat,
uuid: wallet.id
};
var outputs = [];
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
ongoingProcess.set('Processing Transaction...', true);
$timeout(function() {
amazonService.createBitPayInvoice(dataSrc, function(err, dataInvoice) {
if (err) {
ongoingProcess.set('Processing Transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
amazonService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) {
if (err) {
ongoingProcess.set('Processing Transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
$log.debug('Fetch PayPro Request...', invoice.paymentUrls.BIP73);
wallet.fetchPayPro({
payProUrl: invoice.paymentUrls.BIP73,
}, function(err, paypro) {
if (err) {
ongoingProcess.set('Processing Transaction...', false);
$log.warn('Could not fetch payment request:', err);
var msg = err.toString();
if (msg.match('HTTP')) {
msg = gettextCatalog.getString('Could not fetch payment information');
}
popupService.showAlert(gettextCatalog.getString('Error'), msg);
return;
}
if (!paypro.verified) {
ongoingProcess.set('Processing Transaction...', false);
$log.warn('Failed to verify payment protocol signatures');
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Payment Protocol Invalid'));
$timeout(function() {
$scope.$digest();
});
return;
}
var address, comment, amount, url;
address = paypro.toAddress;
amount = paypro.amount;
url = paypro.url;
comment = 'Amazon.com Gift Card';
outputs.push({
'toAddress': address,
'amount': amount,
'message': comment
});
var txp = {
toAddress: address,
amount: amount,
outputs: outputs,
message: comment,
payProUrl: url,
excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
feeLevel: walletSettings.feeLevel || 'normal'
};
walletService.createTx(wallet, txp, function(err, createdTxp) {
ongoingProcess.set('Processing Transaction...', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
return;
}
walletService.publishAndSign(wallet, createdTxp, function(err, tx) {
if (err) {
ongoingProcess.set('Processing Transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
walletService.removeTx(wallet, tx, function(err) {
if (err) $log.debug(err);
});
$timeout(function() {
$scope.$digest();
});
return;
}
var count = 0;
ongoingProcess.set('Processing Transaction...', true);
dataSrc.accessKey = dataInvoice.accessKey;
dataSrc.invoiceId = invoice.id;
dataSrc.invoiceUrl = invoice.url;
dataSrc.invoiceTime = invoice.invoiceTime;
self.debounceCreate(count, dataSrc);
});
});
});
});
});
}, 100);
};
self.debounceCreate = lodash.throttle(function(count, dataSrc) {
self.debounceCreateGiftCard(count, dataSrc);
}, 8000, {
'leading': true
});
self.debounceCreateGiftCard = function(count, dataSrc) {
amazonService.createGiftCard(dataSrc, function(err, giftCard) {
$log.debug("creating gift card " + count);
if (err) {
giftCard = {};
giftCard.status = 'FAILURE';
ongoingProcess.set('Processing Transaction...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err));
self.errorInfo = dataSrc;
$timeout(function() {
$scope.$digest();
});
}
if (giftCard.status == 'PENDING' && count < 3) {
$log.debug("pending gift card not available yet");
self.debounceCreate(count + 1, dataSrc, dataSrc);
return;
}
var now = moment().unix() * 1000;
var newData = giftCard;
newData['invoiceId'] = dataSrc.invoiceId;
newData['accessKey'] = dataSrc.accessKey;
newData['invoiceUrl'] = dataSrc.invoiceUrl;
newData['amount'] = dataSrc.amount;
newData['date'] = dataSrc.invoiceTime || now;
newData['uuid'] = dataSrc.uuid;
if (newData.status == 'expired') {
amazonService.savePendingGiftCard(newData, {
remove: true
}, function(err) {
return;
});
}
amazonService.savePendingGiftCard(newData, null, function(err) {
ongoingProcess.set('Processing Transaction...', false);
$log.debug("Saving new gift card with status: " + newData.status);
self.giftCard = newData;
if (newData.status == 'PENDING') $state.transitionTo('tabs.giftcards.amazon');
$timeout(function() {
$scope.$digest();
});
});
});
};
$scope.$on("$ionicView.enter", function(event, data) {
$scope.formData = {
fiat: null
};
$scope.wallets = profileService.getWallets({
network: network,
onlyComplete: true
});
});
});

View file

@ -1,146 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('buyGlideraController',
function($scope, $timeout, $log, profileService, walletService, glideraService, bwcError, lodash, ongoingProcess, popupService, gettextCatalog) {
var wallet;
var self = this;
this.show2faCodeInput = null;
this.success = null;
$scope.network = glideraService.getEnvironment();
$scope.$on('Wallet/Changed', function(event, w) {
if (lodash.isEmpty(w)) {
$log.debug('No wallet provided');
return;
}
wallet = w;
$log.debug('Wallet changed: ' + w.name);
});
$scope.update = function(opts) {
if (!$scope.token || !$scope.permissions) return;
$log.debug('Updating Glidera Account...');
var accessToken = $scope.token;
var permissions = $scope.permissions;
opts = opts || {};
glideraService.getStatus(accessToken, function(err, data) {
$scope.status = data;
});
glideraService.getLimits(accessToken, function(err, limits) {
$scope.limits = limits;
});
if (permissions.transaction_history) {
glideraService.getTransactions(accessToken, function(err, data) {
$scope.txs = data;
});
}
if (permissions.view_email_address && opts.fullUpdate) {
glideraService.getEmail(accessToken, function(err, data) {
$scope.email = data.email;
});
}
if (permissions.personal_info && opts.fullUpdate) {
glideraService.getPersonalInfo(accessToken, function(err, data) {
$scope.personalInfo = data;
});
}
};
this.getBuyPrice = function(token, price) {
var self = this;
if (!price || (price && !price.qty && !price.fiat)) {
this.buyPrice = null;
return;
}
this.gettingBuyPrice = true;
glideraService.buyPrice(token, price, function(err, buyPrice) {
self.gettingBuyPrice = false;
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get exchange information. Please, try again'));
return;
}
self.buyPrice = buyPrice;
});
};
this.get2faCode = function(token) {
var self = this;
ongoingProcess.set('Sending 2FA code...', true);
$timeout(function() {
glideraService.get2faCode(token, function(err, sent) {
ongoingProcess.set('Sending 2FA code...', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not send confirmation code to your phone'));
return;
}
self.show2faCodeInput = sent;
});
}, 100);
};
this.sendRequest = function(token, permissions, twoFaCode) {
var self = this;
ongoingProcess.set('Buying Bitcoin...', true);
$timeout(function() {
walletService.getAddress(wallet, false, function(err, walletAddr) {
if (err) {
ongoingProcess.set('Buying Bitcoin...', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.cb(err, 'Could not create address'));
return;
}
var data = {
destinationAddress: walletAddr,
qty: self.buyPrice.qty,
priceUuid: self.buyPrice.priceUuid,
useCurrentPrice: false,
ip: null
};
glideraService.buy(token, twoFaCode, data, function(err, data) {
ongoingProcess.set('Buying Bitcoin...', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
self.success = data;
$timeout(function() {
$scope.$digest();
});
});
});
}, 100);
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.token = null;
$scope.permissions = null;
$scope.email = null;
$scope.personalInfo = null;
$scope.txs = null;
$scope.status = null;
$scope.limits = null;
ongoingProcess.set('connectingGlidera', true);
glideraService.init($scope.token, function(err, glidera) {
ongoingProcess.set('connectingGlidera');
if (err || !glidera) {
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
$scope.token = glidera.token;
$scope.permissions = glidera.permissions;
$scope.update({fullUpdate: true});
});
$scope.wallets = profileService.getWallets({
network: $scope.network,
onlyComplete: true
});
});
});

View file

@ -1,103 +1,240 @@
'use strict';
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, gettext, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService) {
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, profileService, bitcore, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, amazonService, glideraService, bwcError, bitpayCardService) {
var cachedTxp = {};
var toAmount;
var isChromeApp = platformInfo.isChromeApp;
var countDown = null;
var giftCardAmountUSD;
var giftCardAccessKey;
var giftCardInvoiceTime;
var giftCardUUID;
var cachedSendMax = {};
$scope.isCordova = platformInfo.isCordova;
$ionicConfig.views.swipeBackEnabled(false);
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isWallet = data.stateParams.isWallet;
// Amazon.com Gift Card parameters
$scope.isGiftCard = data.stateParams.isGiftCard;
giftCardAmountUSD = data.stateParams.giftCardAmountUSD;
giftCardAccessKey = data.stateParams.giftCardAccessKey;
giftCardInvoiceTime = data.stateParams.giftCardInvoiceTime;
giftCardUUID = data.stateParams.giftCardUUID;
// Glidera parameters
$scope.isGlidera = data.stateParams.isGlidera;
$scope.glideraAccessToken = data.stateParams.glideraAccessToken;
toAmount = data.stateParams.toAmount;
cachedSendMax = {};
$scope.useSendMax = data.stateParams.useSendMax == 'true' ? true : false;
var isWallet = data.stateParams.isWallet || 'false';
$scope.isWallet = (isWallet.toString().trim().toLowerCase() == 'true' ? true : false);
$scope.cardId = data.stateParams.cardId;
$scope.toAmount = data.stateParams.toAmount;
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.description = data.stateParams.description;
$scope.paypro = data.stateParams.paypro;
$scope.insufficientFunds = false;
$scope.noMatchingWallet = false;
$scope.paymentExpired = {
value: false
};
$scope.remainingTimeStr = {
value: null
};
initConfirm();
});
var initConfirm = function() {
// TODO (URL , etc)
if (!$scope.toAddress || !$scope.toAmount) {
$log.error('Bad params at amount');
throw ('bad params');
}
var config = configService.getSync().wallet;
$scope.feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
var feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
$scope.feeLevel = feeService.feeOpts[feeLevel];
if ($scope.isGlidera) $scope.network = glideraService.getEnvironment();
else $scope.network = (new bitcore.Address($scope.toAddress)).network.name;
resetValues();
setwallets();
});
$scope.toAmount = parseInt($scope.toAmount);
$scope.amountStr = txFormatService.formatAmountStr($scope.toAmount);
$scope.displayAmount = getDisplayAmount($scope.amountStr);
$scope.displayUnit = getDisplayUnit($scope.amountStr);
var networkName = (new bitcore.Address($scope.toAddress)).network.name;
$scope.network = networkName;
$scope.insuffientFunds = false;
$scope.noMatchingWallet = false;
var wallets = profileService.getWallets({
function setwallets() {
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: networkName,
network: $scope.network
});
if (!wallets || !wallets.length) {
if (!$scope.wallets || !$scope.wallets.length) {
$scope.noMatchingWallet = true;
if ($scope.paypro) {
displayValues();
}
$timeout(function() {
$scope.$apply();
});
return;
}
if ($scope.isGlidera == 'buy') {
initConfirm();
return;
}
var filteredWallets = [];
var index = 0;
var enoughFunds = false;
lodash.each(wallets, function(w) {
lodash.each($scope.wallets, function(w) {
walletService.getStatus(w, {}, function(err, status) {
if (err || !status) {
$log.error(err);
} else {
w.status = status;
if (!status.availableBalanceSat) $log.debug('No balance available in: ' + w.name);
if (status.availableBalanceSat > $scope.toAmount) {
if (status.availableBalanceSat > toAmount) {
filteredWallets.push(w);
enoughFunds = true;
}
}
if (++index == wallets.length) {
if (++index == $scope.wallets.length) {
if (!lodash.isEmpty(filteredWallets)) {
$scope.wallets = lodash.clone(filteredWallets);
setWallet($scope.wallets[0]);
if ($scope.useSendMax) {
if ($scope.wallets.length > 1)
$scope.showWalletSelector();
else {
$scope.wallet = $scope.wallets[0];
$scope.getSendMaxInfo();
}
} else initConfirm();
} else {
if (!enoughFunds)
$scope.insuffientFunds = true;
if (!enoughFunds) $scope.insufficientFunds = true;
$log.warn('No wallet available to make the payment');
$timeout(function() {
$scope.$apply();
});
}
$timeout(function() {
$scope.$apply();
});
}
});
});
};
txFormatService.formatAlternativeStr($scope.toAmount, function(v) {
var initConfirm = function() {
if ($scope.paypro) _paymentTimeControl($scope.paypro.expires);
displayValues();
if ($scope.wallets.length > 1) $scope.showWalletSelector();
else setWallet($scope.wallets[0]);
$timeout(function() {
$scope.$apply();
});
};
function displayValues() {
toAmount = parseInt(toAmount);
$scope.amountStr = txFormatService.formatAmountStr(toAmount);
$scope.displayAmount = getDisplayAmount($scope.amountStr);
$scope.displayUnit = getDisplayUnit($scope.amountStr);
txFormatService.formatAlternativeStr(toAmount, function(v) {
$scope.alternativeAmountStr = v;
});
if ($scope.isGlidera == 'buy') $scope.getBuyPrice();
if ($scope.isGlidera == 'sell') $scope.getSellPrice();
};
if($scope.paypro) {
_paymentTimeControl($scope.paypro.expires);
}
function resetValues() {
$scope.displayAmount = $scope.displayUnit = $scope.fee = $scope.alternativeAmountStr = $scope.insufficientFunds = $scope.noMatchingWallet = null;
};
$scope.getSendMaxInfo = function() {
resetValues();
ongoingProcess.set('gettingFeeLevels', true);
feeService.getCurrentFeeValue($scope.network, function(err, feePerKb) {
ongoingProcess.set('gettingFeeLevels', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message);
return;
}
var config = configService.getSync().wallet;
ongoingProcess.set('retrievingInputs', true);
walletService.getSendMaxInfo($scope.wallet, {
feePerKb: feePerKb,
excludeUnconfirmedUtxos: !config.spendUnconfirmed,
returnInputs: true,
}, function(err, resp) {
ongoingProcess.set('retrievingInputs', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
if (resp.amount == 0) {
$scope.insufficientFunds = true;
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'));
return;
}
$scope.sendMaxInfo = {
sendMax: true,
amount: resp.amount,
inputs: resp.inputs,
fee: resp.fee,
feePerKb: feePerKb,
};
cachedSendMax[$scope.wallet.id] = $scope.sendMaxInfo;
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees.", {
fee: txFormatService.formatAmountStr(resp.fee)
});
var warningMsg = verifyExcludedUtxos();
if (!lodash.isEmpty(warningMsg))
msg += '\n' + warningMsg;
popupService.showAlert(null, msg, function() {
setSendMaxValues(resp);
createTx($scope.wallet, true, function(err, txp) {
if (err) return;
cachedTxp[$scope.wallet.id] = txp;
apply(txp);
});
});
function verifyExcludedUtxos() {
var warningMsg = [];
if (resp.utxosBelowFee > 0) {
warningMsg.push(gettextCatalog.getString("A total of {{amountBelowFeeStr}} were excluded. These funds come from UTXOs smaller than the network fee provided.", {
amountBelowFeeStr: txFormatService.formatAmountStr(resp.amountBelowFee)
}));
}
if (resp.utxosAboveMaxSize > 0) {
warningMsg.push(gettextCatalog.getString("A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded.", {
amountAboveMaxSizeStr: txFormatService.formatAmountStr(resp.amountAboveMaxSize)
}));
}
return warningMsg.join('\n');
};
});
});
};
function setSendMaxValues(data) {
resetValues();
var config = configService.getSync().wallet;
var unitToSatoshi = config.settings.unitToSatoshi;
var satToUnit = 1 / unitToSatoshi;
var unitDecimals = config.settings.unitDecimals;
$scope.amountStr = txFormatService.formatAmountStr(data.amount, true);
$scope.displayAmount = getDisplayAmount($scope.amountStr);
$scope.displayUnit = getDisplayUnit($scope.amountStr);
$scope.fee = txFormatService.formatAmountStr(data.fee);
toAmount = parseFloat((data.amount * satToUnit).toFixed(unitDecimals));
txFormatService.formatAlternativeStr(data.amount, function(v) {
$scope.alternativeAmountStr = v;
});
$timeout(function() {
$scope.$apply();
});
@ -107,24 +244,25 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.approve();
});
$scope.$on('Wallet/Changed', function(event, wallet) {
if (lodash.isEmpty(wallet)) {
$log.debug('No wallet provided');
return;
}
$log.debug('Wallet changed: ' + wallet.name);
setWallet(wallet, true);
});
$scope.showWalletSelector = function() {
$scope.walletSelectorTitle = $scope.isGlidera == 'buy' ? 'Receive in' : $scope.isGlidera == 'sell' ? 'Sell From' : gettextCatalog.getString('Send from');
if (!$scope.useSendMax && ($scope.insufficientFunds || $scope.noMatchingWallet)) return;
$scope.showWallets = true;
};
$scope.onWalletSelect = function(wallet) {
setWallet(wallet);
if ($scope.useSendMax) {
$scope.wallet = wallet;
if (cachedSendMax[wallet.id]) {
$log.debug('Send max cached for wallet:', wallet.id);
setSendMaxValues(cachedSendMax[wallet.id]);
return;
}
$scope.getSendMaxInfo();
} else
setWallet(wallet);
};
$scope.showDescriptionPopup = function() {
var message = gettextCatalog.getString('Add description');
var opts = {
@ -135,17 +273,17 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (typeof res != 'undefined') $scope.description = res;
$timeout(function() {
$scope.$apply();
}, 100);
});
});
};
function getDisplayAmount(amountStr) {
return amountStr.split(' ')[0];
}
return $scope.amountStr.split(' ')[0];
};
function getDisplayUnit(amountStr) {
return amountStr.split(' ')[1];
}
return $scope.amountStr.split(' ')[1];
};
function _paymentTimeControl(expirationTime) {
$scope.paymentExpired.value = false;
@ -167,7 +305,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
var m = Math.floor(totalSecs / 60);
var s = totalSecs % 60;
$scope.remainingTimeStr.value = ('0' + m).slice(-2) + ":" + ('0' + s).slice(-2);
}
};
function setExpiredValues() {
$scope.paymentExpired.value = true;
@ -176,19 +314,15 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$timeout(function() {
$scope.$apply();
});
}
}
};
};
function setWallet(wallet, delayed) {
var stop;
$scope.wallet = wallet;
$scope.fee = $scope.txp = null;
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
if ($scope.isGlidera) return;
if (stop) {
$timeout.cancel(stop);
stop = null;
@ -205,67 +339,75 @@ angular.module('copayApp.controllers').controller('confirmController', function(
});
}, delayed ? 2000 : 1);
}
}
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
};
var setSendError = function(msg) {
$scope.sendStatus = '';
$timeout(function() {
$scope.$apply();
});
popupService.showAlert(gettextCatalog.getString('Error at confirm:'), msg);
popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg));
};
function apply(txp) {
$scope.fee = txFormatService.formatAmountStr(txp.fee);
$scope.txp = txp;
$scope.$apply();
}
$timeout(function() {
$scope.$apply();
});
};
var createTx = function(wallet, dryRun, cb) {
var config = configService.getSync().wallet;
var currentSpendUnconfirmed = config.spendUnconfirmed;
var outputs = [];
var paypro = $scope.paypro;
var toAddress = $scope.toAddress;
var toAmount = $scope.toAmount;
var description = $scope.description;
var unitToSatoshi = config.settings.unitToSatoshi;
var unitDecimals = config.settings.unitDecimals;
// ToDo: use a credential's (or fc's) function for this
if (description && !wallet.credentials.sharedEncryptingKey) {
var msg = 'Could not add message to imported wallet without shared encrypting key';
var msg = gettextCatalog.getString('Could not add message to imported wallet without shared encrypting key');
$log.warn(msg);
return setSendError(msg);
}
if (toAmount > Number.MAX_SAFE_INTEGER) {
var msg = 'Amount too big';
var msg = gettextCatalog.getString('Amount too big');
$log.warn(msg);
return setSendError(msg);
}
outputs.push({
'toAddress': toAddress,
'amount': toAmount,
'message': description
});
var txp = {};
var amount;
// TODO
if (!lodash.isEmpty($scope.sendMaxInfo)) {
txp.sendMax = true;
if ($scope.useSendMax) amount = parseFloat((toAmount * unitToSatoshi).toFixed(0));
else amount = toAmount;
txp.outputs = [{
'toAddress': toAddress,
'amount': amount,
'message': description
}];
if ($scope.sendMaxInfo) {
txp.inputs = $scope.sendMaxInfo.inputs;
txp.fee = $scope.sendMaxInfo.fee;
}
} else
txp.feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
txp.outputs = outputs;
txp.message = description;
if(paypro) {
if (paypro) {
txp.payProUrl = paypro.url;
}
txp.excludeUnconfirmedUtxos = config.spendUnconfirmed ? false : true;
txp.feeLevel = config.settings && config.settings.feeLevel ? config.settings.feeLevel : 'normal';
txp.excludeUnconfirmedUtxos = !currentSpendUnconfirmed;
txp.dryRun = dryRun;
walletService.createTx(wallet, txp, function(err, ctxp) {
@ -288,6 +430,11 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.approve = function(onSendStatusChange) {
var wallet = $scope.wallet;
if (!wallet) {
return;
}
if ($scope.paypro && $scope.paymentExpired.value) {
popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.'));
$scope.sendStatus = '';
@ -297,17 +444,45 @@ angular.module('copayApp.controllers').controller('confirmController', function(
return;
}
var wallet = $scope.wallet;
if (!wallet) {
return setSendError(gettextCatalog.getString('No wallet selected'));
}
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
return walletService.onlyPublish(wallet, txp, function(err, txp) {
if (err) return setSendError(err);
if ($scope.isGlidera) {
$scope.get2faCode(function(err, sent) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not send confirmation code to your phone'));
return;
}
if (sent) {
var title = gettextCatalog.getString("Please, enter the code below");
var message = gettextCatalog.getString("A SMS containing a confirmation code was sent to your phone.");
popupService.showPrompt(title, message, null, function(twoFaCode) {
if (typeof twoFaCode == 'undefined') return;
if ($scope.isGlidera == 'buy') {
$scope.buyRequest(wallet, twoFaCode, function(err, data) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
});
})
}
if ($scope.isGlidera == 'sell') {
$scope.sellRequest(wallet, twoFaCode, function(err, data) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
});
})
}
});
}
});
return;
}
ongoingProcess.set('creatingTx', true, onSendStatusChange);
@ -356,13 +531,18 @@ angular.module('copayApp.controllers').controller('confirmController', function(
function statusChangeHandler(processName, showName, isOn) {
$log.debug('statusChangeHandler: ', processName, showName, isOn);
if ((processName === 'broadcastingTx' || ((processName === 'signingTx') && $scope.wallet.m > 1)) && !isOn) {
if (
(
processName === 'broadcastingTx' ||
((processName === 'signingTx') && $scope.wallet.m > 1) ||
(processName == 'sendingTx' && !$scope.wallet.canSign() && !$scope.wallet.isPrivKeyExternal())
) && !isOn) {
$scope.sendStatus = 'success';
$scope.$digest();
} else if (showName) {
$scope.sendStatus = showName;
}
}
};
$scope.statusChangeHandler = statusChangeHandler;
@ -373,6 +553,8 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.onSuccessConfirm = function() {
var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName;
var fromBitPayCard = previousView.match(/tabs.bitpayCard/) ? true : false;
var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false;
var fromGlidera = previousView.match(/tabs.buyandsell.glidera/) ? true : false;
$ionicHistory.nextViewOptions({
disableAnimate: true
@ -386,14 +568,288 @@ angular.module('copayApp.controllers').controller('confirmController', function(
id: $stateParams.cardId
});
}, 100);
} else if (fromAmazon) {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.giftcards.amazon', {
cardClaimCode: $scope.amazonGiftCard ? $scope.amazonGiftCard.claimCode : null
});
});
} else if (fromGlidera) {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.home').then(function() {
$state.transitionTo('tabs.buyandsell.glidera');
});
} else {
$state.go('tabs.send');
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.send').then(function() {
$state.transitionTo('tabs.home');
});
}
};
$scope.get2faCode = function(cb) {
ongoingProcess.set('sending2faCode', true);
$timeout(function() {
glideraService.get2faCode($scope.glideraAccessToken, function(err, sent) {
ongoingProcess.set('sending2faCode', false);
return cb(err, sent);
});
}, 100);
};
$scope.buyRequest = function(wallet, twoFaCode, cb) {
ongoingProcess.set('buyingBitcoin', true);
$timeout(function() {
walletService.getAddress(wallet, false, function(err, walletAddr) {
if (err) {
ongoingProcess.set('buyingBitcoin', false);
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.cb(err, 'Could not create address'));
return;
}
var data = {
destinationAddress: walletAddr,
qty: $scope.buyPrice.qty,
priceUuid: $scope.buyPrice.priceUuid,
useCurrentPrice: false,
ip: null
};
glideraService.buy($scope.glideraAccessToken, twoFaCode, data, function(err, data) {
ongoingProcess.set('buyingBitcoin', false);
return cb(err, data);
});
});
}, 100);
};
$scope.sellRequest = function(wallet, twoFaCode, cb) {
var outputs = [];
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
ongoingProcess.set('creatingTx', true);
walletService.getAddress(wallet, null, function(err, refundAddress) {
if (!refundAddress) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, 'Could not create address'));
return;
}
glideraService.getSellAddress($scope.glideraAccessToken, function(err, sellAddress) {
if (!sellAddress || err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get the destination bitcoin address'));
return;
}
var amount = parseInt(($scope.sellPrice.qty * 100000000).toFixed(0));
var comment = 'Glidera transaction';
outputs.push({
'toAddress': sellAddress,
'amount': amount,
'message': comment
});
var txp = {
toAddress: sellAddress,
amount: amount,
outputs: outputs,
message: comment,
payProUrl: null,
excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
feeLevel: walletSettings.feeLevel || 'normal',
customData: {
'glideraToken': $scope.glideraAccessToken
}
};
walletService.createTx(wallet, txp, function(err, createdTxp) {
ongoingProcess.clear();
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
walletService.prepare(wallet, function(err, password) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
ongoingProcess.set('signingTx', true);
walletService.publishTx(wallet, createdTxp, function(err, publishedTxp) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message ||  bwcError.msg(err));
return;
}
walletService.signTx(wallet, publishedTxp, password, function(err, signedTxp) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message ||  bwcError.msg(err));
walletService.removeTx(wallet, signedTxp, function(err) {
if (err) $log.debug(err);
});
return;
}
var rawTx = signedTxp.raw;
var data = {
refundAddress: refundAddress,
signedTransaction: rawTx,
priceUuid: $scope.sellPrice.priceUuid,
useCurrentPrice: $scope.sellPrice.priceUuid ? false : true,
ip: null
};
ongoingProcess.set('sellingBitcoin', true);
glideraService.sell($scope.glideraAccessToken, twoFaCode, data, function(err, data) {
ongoingProcess.clear();
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message ||  bwcError.msg(err));
return;
}
return cb(err, data)
});
});
});
});
});
});
});
}
$scope.getBuyPrice = function() {
var satToBtc = 1 / 100000000;
var price = {};
price.qty = (toAmount * satToBtc).toFixed(8);
glideraService.buyPrice($scope.glideraAccessToken, price, function(err, buyPrice) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), 'Could not get exchange information. Please, try again');
return;
}
$scope.buyPrice = buyPrice;
});
};
$scope.getSellPrice = function() {
var satToBtc = 1 / 100000000;
var price = {};
price.qty = (toAmount * satToBtc).toFixed(8);
glideraService.sellPrice($scope.glideraAccessToken, price, function(err, sellPrice) {
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), 'Could not get exchange information. Please, try again');
return;
}
$scope.sellPrice = sellPrice;
});
};
function publishAndSign(wallet, txp, onSendStatusChange) {
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
$log.info('No signing proposal: No private key');
return walletService.onlyPublish(wallet, txp, function(err) {
if (err) setSendError(err);
}, onSendStatusChange);
}
walletService.publishAndSign(wallet, txp, function(err, txp) {
if (err) return setSendError(err);
var previousView = $ionicHistory.viewHistory().backView && $ionicHistory.viewHistory().backView.stateName;
var fromAmazon = previousView.match(/tabs.giftcards.amazon/) ? true : false;
if (fromAmazon) {
var count = 0;
var invoiceId = JSON.parse($scope.paypro.merchant_data).invoiceId;
var dataSrc = {
currency: 'USD',
amount: giftCardAmountUSD,
uuid: giftCardUUID,
accessKey: giftCardAccessKey,
invoiceId: invoiceId,
invoiceUrl: $scope.paypro.url,
invoiceTime: giftCardInvoiceTime
};
debounceCreate(count, dataSrc, onSendStatusChange);
}
}, onSendStatusChange);
}
var debounceCreate = lodash.throttle(function(count, dataSrc) {
debounceCreateGiftCard(count, dataSrc);
}, 8000, {
'leading': true
});
var debounceCreateGiftCard = function(count, dataSrc, onSendStatusChange) {
amazonService.createGiftCard(dataSrc, function(err, giftCard) {
$log.debug("creating gift card " + count);
if (err) {
giftCard = {};
giftCard.status = 'FAILURE';
popupService.showAlert(gettextCatalog.getString('Error'), err);
}
if (giftCard.status == 'PENDING' && count < 3) {
$log.debug("pending gift card not available yet");
debounceCreate(count + 1, dataSrc);
return;
}
var now = moment().unix() * 1000;
var newData = giftCard;
newData['invoiceId'] = dataSrc.invoiceId;
newData['accessKey'] = dataSrc.accessKey;
newData['invoiceUrl'] = dataSrc.invoiceUrl;
newData['amount'] = dataSrc.amount;
newData['date'] = dataSrc.invoiceTime || now;
newData['uuid'] = dataSrc.uuid;
if (newData.status == 'expired') {
amazonService.savePendingGiftCard(newData, {
remove: true
}, function(err) {
$log.error(err);
return;
});
}
amazonService.savePendingGiftCard(newData, null, function(err) {
$log.debug("Saving new gift card with status: " + newData.status);
$scope.amazonGiftCard = newData;
});
});
};
$scope.getRates = function() {
var config = configService.getSync().wallet.settings;
var unitName = config.unitName;
var alternativeIsoCode = config.alternativeIsoCode;
bitpayCardService.getRates(alternativeIsoCode, function(err, res) {
if (err) {
$log.warn(err);
return;
}
if (unitName == 'bits') {
$scope.exchangeRate = '1,000,000 bits ~ ' + res.rate + ' ' + alternativeIsoCode;
} else {
$scope.exchangeRate = '1 BTC ~ ' + res.rate + ' ' + alternativeIsoCode;
}
});
};
});

View file

@ -79,8 +79,8 @@ angular.module('copayApp.controllers').controller('copayersController',
};
$scope.goHome = function() {
$ionicHistory.removeBackView();
$state.go('tabs.home');
$ionicHistory.clearHistory();
};
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('createController',
function($scope, $rootScope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettext, gettextCatalog, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, storageService, popupService) {
function($scope, $rootScope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettextCatalog, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, storageService, popupService, $window) {
var isChromeApp = platformInfo.isChromeApp;
var isCordova = platformInfo.isCordova;
@ -33,7 +33,6 @@ angular.module('copayApp.controllers').controller('createController',
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.setTotalCopayers(tc);
updateRCSelect(tc);
updateSeedSourceSelect(tc);
};
$scope.showAdvChange = function() {
@ -44,7 +43,7 @@ angular.module('copayApp.controllers').controller('createController',
$scope.resizeView = function() {
$timeout(function() {
$ionicScrollDelegate.resize();
});
}, 10);
checkPasswordFields();
};
@ -67,26 +66,35 @@ angular.module('copayApp.controllers').controller('createController',
function updateSeedSourceSelect(n) {
var seedOptions = [{
id: 'new',
label: gettext('Random'),
label: gettextCatalog.getString('Random'),
}, {
id: 'set',
label: gettext('Specify Recovery Phrase...'),
label: gettextCatalog.getString('Specify Recovery Phrase...'),
}];
$scope.seedSource = seedOptions[0];
if (n > 1 && isChromeApp)
seedOptions.push({
id: 'ledger',
label: 'Ledger Hardware Wallet',
});
/*
if (isChromeApp || isDevel) {
seedOptions.push({
id: 'trezor',
label: 'Trezor Hardware Wallet',
});
Disable Hardware Wallets for BitPay distribution
*/
if ($window.appConfig.name == 'copay') {
if (n > 1 && isChromeApp) {
seedOptions.push({
id: 'ledger',
label: 'Ledger Hardware Wallet',
});
}
if (isChromeApp || isDevel) {
seedOptions.push({
id: 'trezor',
label: 'Trezor Hardware Wallet',
});
}
}
$scope.seedOptions = seedOptions;
};

View file

@ -0,0 +1,27 @@
'use strict';
angular.module('copayApp.controllers').controller('customAmountController', function($rootScope, $scope, $stateParams, $ionicHistory, txFormatService, platformInfo) {
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var satToBtc = 1 / 100000000;
$scope.isCordova = platformInfo.isCordova;
$scope.address = data.stateParams.toAddress;
$scope.amount = parseInt(data.stateParams.toAmount);
$scope.amountBtc = ($scope.amount * satToBtc).toFixed(8);
$scope.amountStr = txFormatService.formatAmountStr($scope.amount);
$scope.altAmountStr = txFormatService.formatAlternativeStr($scope.amount);
});
$scope.shareAddress = function(uri) {
window.plugins.socialsharing.share(uri, null, null, null);
};
$scope.finish = function() {
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
$ionicHistory.goBack(-2);
};
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('exportController',
function($scope, $timeout, $log, $ionicHistory, $ionicScrollDelegate, backupService, walletService, storageService, profileService, platformInfo, gettextCatalog, $state, $stateParams, popupService) {
function($scope, $timeout, $log, $ionicHistory, $ionicScrollDelegate, backupService, walletService, storageService, profileService, platformInfo, gettextCatalog, $state, $stateParams, popupService, $window) {
var wallet = profileService.getWallet($stateParams.walletId);
$scope.showAdvChange = function() {
@ -12,7 +12,7 @@ angular.module('copayApp.controllers').controller('exportController',
$scope.resizeView = function() {
$timeout(function() {
$ionicScrollDelegate.resize();
});
}, 10);
};
function getPassword(cb) {
@ -191,7 +191,7 @@ angular.module('copayApp.controllers').controller('exportController',
if ($scope.formData.noSignEnabled)
name = name + '(No Private Key)';
var subject = 'Copay Wallet Backup: ' + name;
var subject = $window.appConfig.nameCase + ' Wallet Backup: ' + name;
var body = 'Here is the encrypted backup of the wallet ' + name + ': \n\n' + ew + '\n\n To import this backup, copy all text between {...}, including the symbols {}';
window.plugins.socialsharing.shareViaEmail(
body,

View file

@ -0,0 +1,130 @@
'use strict';
angular.module('copayApp.controllers').controller('completeController', function($scope, $stateParams, $timeout, $log, $ionicHistory, $state, $ionicNavBarDelegate, $ionicConfig, platformInfo, configService, storageService, lodash) {
$scope.isCordova = platformInfo.isCordova;
var defaults = configService.getDefaults();
function quickFeedback(cb) {
window.plugins.spinnerDialog.show();
$timeout(window.plugins.spinnerDialog.hide, 300);
$timeout(cb, 20);
}
$scope.shareFacebook = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareVia($scope.shareFacebookVia, null, null, null, defaults.download.url);
});
};
$scope.shareTwitter = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareVia($scope.shareTwitterVia, null, null, null, defaults.download.url);
});
};
$scope.shareGooglePlus = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareVia($scope.shareGooglePlusVia, defaults.download.url);
});
};
$scope.shareEmail = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareViaEmail(defaults.download.url);
});
};
$scope.shareWhatsapp = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareViaWhatsApp(defaults.download.url);
});
};
$scope.shareMessage = function() {
quickFeedback(function() {
window.plugins.socialsharing.shareViaSMS(defaults.download.url);
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.score = (data.stateParams && data.stateParams.score) ? parseInt(data.stateParams.score) : null;
$scope.skipped = (data.stateParams && data.stateParams.skipped) ? true : false;
$scope.rated = (data.stateParams && data.stateParams.rated) ? true : false;
$scope.fromSettings = (data.stateParams && data.stateParams.fromSettings) ? true : false;
if (!$scope.fromSettings) {
$ionicConfig.views.swipeBackEnabled(false);
} else {
$ionicNavBarDelegate.showBackButton(true);
$ionicConfig.views.swipeBackEnabled(true);
}
storageService.getFeedbackInfo(function(error, info) {
var feedbackInfo = lodash.isString(info) ? JSON.parse(info) : null;
feedbackInfo.sent = true;
storageService.setFeedbackInfo(JSON.stringify(feedbackInfo), function() {});
});
if (!$scope.isCordova) return;
$scope.animate = true;
window.plugins.socialsharing.available(function(isAvailable) {
// the boolean is only false on iOS < 6
$scope.socialsharing = isAvailable;
if (isAvailable) {
window.plugins.socialsharing.canShareVia('com.apple.social.facebook', 'msg', null, null, null, function(e) {
$scope.shareFacebookVia = 'com.apple.social.facebook';
$scope.facebook = true;
}, function(e) {
window.plugins.socialsharing.canShareVia('com.facebook.katana', 'msg', null, null, null, function(e) {
$scope.shareFacebookVia = 'com.facebook.katana';
$scope.facebook = true;
}, function(e) {
$log.debug('facebook error: ' + e);
$scope.facebook = false;
});
});
window.plugins.socialsharing.canShareVia('com.apple.social.twitter', 'msg', null, null, null, function(e) {
$scope.shareTwitterVia = 'com.apple.social.twitter';
$scope.twitter = true;
}, function(e) {
window.plugins.socialsharing.canShareVia('com.twitter.android', 'msg', null, null, null, function(e) {
$scope.shareTwitterVia = 'com.twitter.android';
$scope.twitter = true;
}, function(e) {
$log.debug('twitter error: ' + e);
$scope.twitter = false;
});
});
window.plugins.socialsharing.canShareVia('com.google.android.apps.plus', 'msg', null, null, null, function(e) {
$scope.shareGooglePlusVia = 'com.google.android.apps.plus';
$scope.googleplus = true;
}, function(e) {
$log.debug('googlePlus error: ' + e);
$scope.googleplus = false;
});
window.plugins.socialsharing.canShareViaEmail(function(e) {
$scope.email = true;
}, function(e) {
$log.debug('email error: ' + e);
$scope.email = false;
});
window.plugins.socialsharing.canShareVia('whatsapp', 'msg', null, null, null, function(e) {
$scope.whatsapp = true;
}, function(e) {
$log.debug('whatsapp error: ' + e);
$scope.whatsapp = false;
});
}
}, 100);
});
$scope.close = function() {
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
if ($scope.score == 5) $ionicHistory.goBack(-3);
else $ionicHistory.goBack(-2);
};
});

View file

@ -0,0 +1,50 @@
'use strict';
angular.module('copayApp.controllers').controller('rateAppController', function($scope, $state, $stateParams, $window, lodash, externalLinkService, configService, platformInfo, feedbackService, ongoingProcess, popupService) {
$scope.score = parseInt($stateParams.score);
var isAndroid = platformInfo.isAndroid;
var isIOS = platformInfo.isIOS;
var isWP = platformInfo.isWP;
var config = configService.getSync();
$scope.skip = function() {
var dataSrc = {
"Email": lodash.values(config.emailFor)[0] || ' ',
"Feedback": ' ',
"Score": $stateParams.score,
"AppVersion": $window.version,
"Platform": ionic.Platform.platform(),
"DeviceVersion": ionic.Platform.version()
};
feedbackService.send(dataSrc, function(err) {
if (err) {
// try to send, but not essential, since the user didn't add a message
$log.warn('Could not send feedback.');
}
});
$state.go('tabs.rate.complete', {
score: $stateParams.score,
skipped: true
});
};
$scope.sendFeedback = function() {
$state.go('tabs.rate.send', {
score: $scope.score
});
};
$scope.goAppStore = function() {
var defaults = configService.getDefaults();
var url;
if (isAndroid) url = defaults.rateApp.android;
if (isIOS) url = defaults.rateApp.ios;
// if (isWP) url = defaults.rateApp.windows; // TODO
externalLinkService.open(url);
$state.go('tabs.rate.complete', {
score: $stateParams.score,
skipped: true,
rated: true
});
};
});

View file

@ -0,0 +1,59 @@
'use strict';
angular.module('copayApp.controllers').controller('rateCardController', function($scope, $state, $timeout, $log, gettextCatalog, platformInfo, storageService) {
$scope.isCordova = platformInfo.isCordova;
$scope.score = 0;
$scope.goFeedbackFlow = function() {
$scope.hideCard();
if ($scope.isCordova && $scope.score == 5) {
$state.go('tabs.rate.rateApp', {
score: $scope.score
});
} else {
$state.go('tabs.rate.send', {
score: $scope.score
});
}
};
$scope.setScore = function(score) {
$scope.score = score;
switch ($scope.score) {
case 1:
$scope.button_title = gettextCatalog.getString("I think this app is terrible.");
break;
case 2:
$scope.button_title = gettextCatalog.getString("I don't like it");
break;
case 3:
$scope.button_title = gettextCatalog.getString("Meh - it's alright");
break;
case 4:
$scope.button_title = gettextCatalog.getString("I like the app");
break;
case 5:
$scope.button_title = gettextCatalog.getString("This app is fantastic!");
break;
}
$timeout(function() {
$scope.$apply();
});
};
$scope.hideCard = function() {
$log.debug('Feedback card dismissed.')
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

@ -0,0 +1,92 @@
'use strict';
angular.module('copayApp.controllers').controller('sendController', function($scope, $state, $log, $timeout, $stateParams, $ionicNavBarDelegate, $ionicHistory, $ionicConfig, $window, gettextCatalog, popupService, configService, lodash, feedbackService, ongoingProcess) {
$scope.sendFeedback = function(feedback, goHome) {
var config = configService.getSync();
var dataSrc = {
"Email": lodash.values(config.emailFor)[0] || ' ',
"Feedback": goHome ? ' ' : feedback,
"Score": $stateParams.score || ' ',
"AppVersion": $window.version,
"Platform": ionic.Platform.platform(),
"DeviceVersion": ionic.Platform.version()
};
if (!goHome) ongoingProcess.set('sendingFeedback', true);
feedbackService.send(dataSrc, function(err) {
if (goHome) return;
ongoingProcess.set('sendingFeedback', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Feedback could not be submitted. Please try again later.'));
return;
}
if (!$stateParams.score) {
popupService.showAlert(gettextCatalog.getString('Thank you!'), gettextCatalog.getString('A member of the team will review your feedback as soon as possible.'), function() {
$scope.feedback.value = '';
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
$ionicHistory.goBack();
}, gettextCatalog.getString('Finish'));
return;
}
$state.go('tabs.rate.complete', {
score: $stateParams.score
});
});
if (goHome) $state.go('tabs.home');
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.score = (data.stateParams && data.stateParams.score) ? parseInt(data.stateParams.score) : null;
$scope.feedback = {};
if ($scope.score) {
$ionicConfig.views.swipeBackEnabled(false);
}
switch ($scope.score) {
case 1:
$scope.reaction = "Ouch!";
$scope.comment = gettextCatalog.getString("There's obviously something we're doing wrong.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 2:
$scope.reaction = gettextCatalog.getString("Oh no!");
$scope.comment = gettextCatalog.getString("There's obviously something we're doing wrong.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 3:
$scope.reaction = "Hmm...";
$scope.comment = gettextCatalog.getString("We'd love to do better.") + ' ' + gettextCatalog.getString("How could we improve your experience?");
break;
case 4:
$scope.reaction = gettextCatalog.getString("Thanks!");
$scope.comment = gettextCatalog.getString("That's exciting to hear. We'd love to earn that fifth star from you how could we improve your experience?");
break;
case 5:
$scope.reaction = gettextCatalog.getString("Thank you!");
$scope.comment = gettextCatalog.getString("We're always looking for ways to improve BitPay.") + ' ' + gettextCatalog.getString("Is there anything we could do better?");
break;
default:
$scope.justFeedback = true;
$scope.comment = gettextCatalog.getString("We're always looking for ways to improve BitPay. How could we improve your experience?");
break;
}
});
$scope.$on("$ionicView.afterEnter", function() {
$scope.showForm = true;
});
$scope.goBack = function() {
$ionicHistory.nextViewOptions({
disableAnimate: false,
historyRoot: true
});
$ionicHistory.goBack();
};
});

View file

@ -18,9 +18,11 @@ angular.module('copayApp.controllers').controller('glideraController',
$scope.status = null;
$scope.limits = null;
$scope.connectingGlidera = true;
ongoingProcess.set('connectingGlidera', true);
glideraService.init($scope.token, function(err, glidera) {
ongoingProcess.set('connectingGlidera');
$scope.connectingGlidera = false;
if (err || !glidera) {
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
@ -67,11 +69,11 @@ angular.module('copayApp.controllers').controller('glideraController',
}
};
this.getAuthenticateUrl = function() {
$scope.getAuthenticateUrl = function() {
return glideraService.getOauthCodeUrl();
};
this.submitOauthCode = function(code) {
$scope.submitOauthCode = function(code) {
ongoingProcess.set('connectingGlidera', true);
$timeout(function() {
glideraService.getToken(code, function(err, data) {
@ -90,10 +92,7 @@ angular.module('copayApp.controllers').controller('glideraController',
}, 100);
};
this.openTxModal = function(token, tx) {
var self = this;
$scope.self = self;
$scope.openTxModal = function(token, tx) {
$scope.tx = tx;
glideraService.getTransaction(token, tx.transactionUuid, function(err, tx) {

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

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('importController',
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, profileService, configService, sjcl, ledger, trezor, derivationPathHelper, platformInfo, bwcService, ongoingProcess, walletService, popupService, gettextCatalog) {
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, $ionicScrollDelegate, profileService, configService, sjcl, ledger, trezor, derivationPathHelper, platformInfo, bwcService, ongoingProcess, walletService, popupService, gettextCatalog, $window) {
var isChromeApp = platformInfo.isChromeApp;
var isDevel = platformInfo.isDevel;
@ -17,6 +17,7 @@ angular.module('copayApp.controllers').controller('importController',
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.account = 1;
$scope.importErr = false;
$scope.showHardwareWallet = $window.appConfig.name == 'copay';
if ($stateParams.code)
$scope.processWalletInfo($stateParams.code);
@ -359,7 +360,11 @@ angular.module('copayApp.controllers').controller('importController',
$scope.resizeView = function() {
$timeout(function() {
$ionicScrollDelegate.resize();
});
}, 10);
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.init();
});
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('joinController',
function($scope, $rootScope, $timeout, $state, $ionicHistory, $ionicScrollDelegate, profileService, configService, storageService, applicationService, gettext, gettextCatalog, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log, $stateParams, popupService) {
function($scope, $rootScope, $timeout, $state, $ionicHistory, $ionicScrollDelegate, profileService, configService, storageService, applicationService, gettextCatalog, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log, $stateParams, popupService, $window) {
var isChromeApp = platformInfo.isChromeApp;
var isDevel = platformInfo.isDevel;
@ -20,7 +20,7 @@ angular.module('copayApp.controllers').controller('joinController',
$scope.resizeView = function() {
$timeout(function() {
$ionicScrollDelegate.resize();
});
}, 10);
checkPasswordFields();
};
@ -50,26 +50,33 @@ angular.module('copayApp.controllers').controller('joinController',
var updateSeedSourceSelect = function() {
self.seedOptions = [{
id: 'new',
label: gettext('Random'),
label: gettextCatalog.getString('Random'),
}, {
id: 'set',
label: gettext('Specify Recovery Phrase...'),
label: gettextCatalog.getString('Specify Recovery Phrase...'),
}];
$scope.seedSource = self.seedOptions[0];
/*
if (isChromeApp) {
self.seedOptions.push({
id: 'ledger',
label: 'Ledger Hardware Wallet',
});
}
Disable Hardware Wallets
if (isChromeApp || isDevel) {
self.seedOptions.push({
id: 'trezor',
label: 'Trezor Hardware Wallet',
});
*/
if ($window.appConfig.name == 'copay') {
if (isChromeApp) {
self.seedOptions.push({
id: 'ledger',
label: 'Ledger Hardware Wallet',
});
}
if (isChromeApp || isDevel) {
self.seedOptions.push({
id: 'trezor',
label: 'Trezor Hardware Wallet',
});
}
}
};

View file

@ -62,8 +62,8 @@ angular.module('copayApp.controllers').controller('amazonCardDetailsController',
$scope.amazonCardDetailsModal.hide();
};
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
externalLinkService.open(url, optIn, title, message, okText, cancelText);
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
});

View file

@ -1,79 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('customAmountController', function($scope, $timeout, $filter, platformInfo, rateService) {
var self = $scope.self;
$scope.unitName = self.unitName;
$scope.alternativeAmount = self.alternativeAmount;
$scope.alternativeName = self.alternativeName;
$scope.alternativeIsoCode = self.alternativeIsoCode;
$scope.isRateAvailable = self.isRateAvailable;
$scope.unitToSatoshi = self.unitToSatoshi;
$scope.unitDecimals = self.unitDecimals;
var satToUnit = 1 / self.unitToSatoshi;
$scope.showAlternative = false;
$scope.isCordova = platformInfo.isCordova;
Object.defineProperty($scope,
"_customAlternative", {
get: function() {
return $scope.customAlternative;
},
set: function(newValue) {
$scope.customAlternative = newValue;
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
$scope.customAmount = parseFloat((rateService.fromFiat(newValue, $scope.alternativeIsoCode) * satToUnit).toFixed($scope.unitDecimals), 10);
} else {
$scope.customAmount = null;
}
},
enumerable: true,
configurable: true
});
Object.defineProperty($scope,
"_customAmount", {
get: function() {
return $scope.customAmount;
},
set: function(newValue) {
$scope.customAmount = newValue;
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
$scope.customAlternative = parseFloat((rateService.toFiat(newValue * $scope.unitToSatoshi, $scope.alternativeIsoCode)).toFixed(2), 10);
} else {
$scope.customAlternative = null;
}
$scope.alternativeAmount = $scope.customAlternative;
},
enumerable: true,
configurable: true
});
$scope.submitForm = function(form) {
var satToBtc = 1 / 100000000;
var amount = form.amount.$modelValue;
var amountSat = parseInt((amount * $scope.unitToSatoshi).toFixed(0));
$timeout(function() {
$scope.customizedAmountUnit = amount + ' ' + $scope.unitName;
$scope.customizedAlternativeUnit = $filter('formatFiatAmount')(form.alternative.$modelValue) + ' ' + $scope.alternativeIsoCode;
if ($scope.unitName == 'bits') {
amount = (amountSat * satToBtc).toFixed(8);
}
$scope.customizedAmountBtc = amount;
}, 1);
};
$scope.toggleAlternative = function() {
$scope.showAlternative = !$scope.showAlternative;
};
$scope.shareAddress = function(uri) {
if (platformInfo.isCordova) {
window.plugins.socialsharing.share(uri, null, null, null);
}
};
$scope.cancel = function() {
$scope.customAmountModal.hide();
};
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('searchController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $ionicNavBarDelegate, $state, $stateParams, $ionicScrollDelegate, bwcError, profileService, lodash, configService, gettext, gettextCatalog, platformInfo, walletService) {
angular.module('copayApp.controllers').controller('searchController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicScrollDelegate, bwcError, profileService, lodash, configService, gettext, gettextCatalog, platformInfo, walletService) {
var HISTORY_SHOW_LIMIT = 10;
var currentTxHistoryPage = 0;

View file

@ -1,9 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('txpDetailsController', function($scope, $rootScope, $timeout, $interval, $ionicModal, $log, ongoingProcess, platformInfo, $ionicScrollDelegate, txFormatService, fingerprintService, bwcError, gettextCatalog, lodash, walletService, popupService, $state, $ionicHistory) {
var self = $scope.self;
var tx = $scope.tx;
var copayers = $scope.copayers;
angular.module('copayApp.controllers').controller('txpDetailsController', function($scope, $rootScope, $timeout, $interval, $log, ongoingProcess, platformInfo, $ionicScrollDelegate, txFormatService, bwcError, gettextCatalog, lodash, walletService, popupService, $ionicHistory) {
var isGlidera = $scope.isGlidera;
var GLIDERA_LOCK_TIME = 6 * 60 * 60;
var now = Math.floor(Date.now() / 1000);
@ -18,8 +15,8 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
$scope.color = $scope.wallet.color;
$scope.data = {};
$scope.hasClick = platformInfo.hasClick;
$scope.displayAmount = getDisplayAmount(tx.amountStr);
$scope.displayUnit = getDisplayUnit(tx.amountStr);
$scope.displayAmount = getDisplayAmount($scope.tx.amountStr);
$scope.displayUnit = getDisplayUnit($scope.tx.amountStr);
initActionList();
checkPaypro();
}
@ -46,12 +43,12 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
$scope.actionList.push({
type: 'created',
time: tx.createdOn,
time: $scope.tx.createdOn,
description: actionDescriptions['created'],
by: tx.creatorName
by: $scope.tx.creatorName
});
lodash.each(tx.actions, function(action) {
lodash.each($scope.tx.actions, function(action) {
$scope.actionList.push({
type: action.type,
time: action.createdOn,
@ -61,103 +58,14 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
});
};
$scope.$on('accepted', function(event) {
$scope.sign();
});
// ToDo: use tx.customData instead of tx.message
if (tx.message === 'Glidera transaction' && isGlidera) {
tx.isGlidera = true;
if (tx.canBeRemoved) {
tx.canBeRemoved = (Date.now() / 1000 - (tx.ts || tx.createdOn)) > GLIDERA_LOCK_TIME;
}
}
var setSendError = function(msg) {
$scope.sendStatus = '';
var error = msg || gettextCatalog.getString('Could not send payment');
popupService.showAlert(gettextCatalog.getString('Error'), error);
}
$scope.sign = function(onSendStatusChange) {
$scope.loading = true;
walletService.publishAndSign($scope.wallet, $scope.tx, function(err, txp) {
$scope.$emit('UpdateTx');
if (err) return setSendError(err);
success();
}, onSendStatusChange);
};
function setError(err, prefix) {
$scope.loading = false;
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, prefix));
};
$scope.reject = function(txp) {
var title = gettextCatalog.getString('Warning!');
var msg = gettextCatalog.getString('Are you sure you want to reject this transaction?');
popupService.showConfirm(title, msg, null, null, function(res) {
if (res) {
$scope.loading = true;
walletService.reject($scope.wallet, $scope.tx, function(err, txpr) {
if (err)
return setError(err, gettextCatalog.getString('Could not reject payment'));
$scope.close();
});
}
});
};
$scope.remove = function() {
$scope.loading = true;
$timeout(function() {
ongoingProcess.set('removeTx', true);
walletService.removeTx($scope.wallet, $scope.tx, function(err) {
ongoingProcess.set('removeTx', false);
// Hacky: request tries to parse an empty response
if (err && !(err.message && err.message.match(/Unexpected/))) {
$scope.$emit('UpdateTx');
return setError(err, gettextCatalog.getString('Could not delete payment proposal'));
}
$scope.close();
});
}, 10);
};
$scope.broadcast = function(txp) {
$scope.loading = true;
$timeout(function() {
ongoingProcess.set('broadcastTx', true);
walletService.broadcastTx($scope.wallet, $scope.tx, function(err, txpb) {
ongoingProcess.set('broadcastTx', false);
if (err) {
return setError(err, gettextCatalog.getString('Could not broadcast payment'));
}
$scope.close();
});
}, 10);
};
$scope.getShortNetworkName = function() {
return $scope.wallet.credentials.networkName.substring(0, 4);
};
function checkPaypro() {
if (tx.payProUrl && !platformInfo.isChromeApp) {
if ($scope.tx.payProUrl && !platformInfo.isChromeApp) {
$scope.wallet.fetchPayPro({
payProUrl: tx.payProUrl,
payProUrl: $scope.tx.payProUrl,
}, function(err, paypro) {
if (err) return;
tx.paypro = paypro;
paymentTimeControl(tx.paypro.expires);
$scope.tx.paypro = paypro;
paymentTimeControl($scope.tx.paypro.expires);
$timeout(function() {
$ionicScrollDelegate.resize();
}, 10);
@ -187,32 +95,132 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
};
lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy', 'transactionProposalRemoved', 'TxProposalRemoved', 'NewOutgoingTx', 'UpdateTx'], function(eventName) {
$rootScope.$on(eventName, function() {
$scope.wallet.getTx($scope.tx.id, function(err, tx) {
if (err) {
if (err.message && err.message == 'TX_NOT_FOUND' &&
(eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) {
$scope.tx.removed = true;
$scope.tx.canBeRemoved = false;
$scope.tx.pendingForUs = false;
$scope.$apply();
$scope.$on('accepted', function(event) {
$scope.sign();
});
// ToDo: use tx.customData instead of tx.message
if ($scope.tx.message === 'Glidera transaction' && isGlidera) {
$scope.tx.isGlidera = true;
if ($scope.tx.canBeRemoved) {
$scope.tx.canBeRemoved = (Date.now() / 1000 - ($scope.tx.ts || $scope.tx.createdOn)) > GLIDERA_LOCK_TIME;
}
}
var setError = function(err, prefix) {
$scope.sendStatus = '';
$scope.loading = false;
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, prefix));
};
$scope.sign = function(onSendStatusChange) {
$scope.loading = true;
walletService.publishAndSign($scope.wallet, $scope.tx, function(err, txp) {
$scope.$emit('UpdateTx');
if (err) return setError(err, gettextCatalog.getString('Could not send payment'));
success();
}, onSendStatusChange);
};
$scope.reject = function(txp) {
var title = gettextCatalog.getString('Warning!');
var msg = gettextCatalog.getString('Are you sure you want to reject this transaction?');
popupService.showConfirm(title, msg, null, null, function(res) {
if (res) {
$scope.loading = true;
walletService.reject($scope.wallet, $scope.tx, function(err, txpr) {
if (err)
return setError(err, gettextCatalog.getString('Could not reject payment'));
$scope.close();
});
}
});
};
$scope.remove = function() {
var title = gettextCatalog.getString('Warning!');
var msg = gettextCatalog.getString('Are you sure you want to remove this transaction?');
popupService.showConfirm(title, msg, null, null, function(res) {
if (res) {
ongoingProcess.set('removeTx', true);
walletService.removeTx($scope.wallet, $scope.tx, function(err) {
ongoingProcess.set('removeTx', false);
// Hacky: request tries to parse an empty response
if (err && !(err.message && err.message.match(/Unexpected/))) {
$scope.$emit('UpdateTx');
return setError(err, gettextCatalog.getString('Could not delete payment proposal'));
}
return;
$scope.close();
});
}
});
};
$scope.broadcast = function(txp) {
$scope.loading = true;
$timeout(function() {
ongoingProcess.set('broadcastingTx', true);
walletService.broadcastTx($scope.wallet, $scope.tx, function(err, txpb) {
ongoingProcess.set('broadcastingTx', false);
if (err) {
return setError(err, gettextCatalog.getString('Could not broadcast payment'));
}
var action = lodash.find(tx.actions, {
copayerId: $scope.wallet.credentials.copayerId
});
$scope.tx = txFormatService.processTx(tx);
if (!action && tx.status == 'pending')
$scope.tx.pendingForUs = true;
$scope.updateCopayerList();
$scope.$apply();
$scope.close();
});
}, 10);
};
$scope.getShortNetworkName = function() {
return $scope.wallet.credentials.networkName.substring(0, 4);
};
var updateTxInfo = function(eventName) {
$scope.wallet.getTx($scope.tx.id, function(err, tx) {
if (err) {
if (err.message && err.message == 'Transaction proposal not found' &&
(eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) {
$scope.tx.removed = true;
$scope.tx.canBeRemoved = false;
$scope.tx.pendingForUs = false;
$scope.$apply();
}
return;
}
var action = lodash.find(tx.actions, {
copayerId: $scope.wallet.credentials.copayerId
});
$scope.tx = txFormatService.processTx(tx);
if (!action && tx.status == 'pending')
$scope.tx.pendingForUs = true;
$scope.updateCopayerList();
initActionList();
$scope.$apply();
});
};
var bwsEvent = $rootScope.$on('bwsEvent', function(e, walletId, type, n) {
lodash.each([
'TxProposalRejectedBy',
'TxProposalAcceptedBy',
'transactionProposalRemoved',
'TxProposalRemoved',
'NewOutgoingTx',
'UpdateTx'
], function(eventName) {
if (walletId == $scope.wallet.id && type == eventName) {
updateTxInfo(eventName);
}
});
});
@ -252,6 +260,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
$scope.close = function() {
bwsEvent();
$scope.loading = null;
$scope.txpDetailsModal.hide();
};

View file

@ -9,8 +9,8 @@ angular.module('copayApp.controllers').controller('backupWarningController', fun
$scope.openPopup = function() {
$ionicModal.fromTemplateUrl('views/includes/screenshotWarningModal.html', {
scope: $scope,
backdropClickToClose: false,
hardwareBackButtonClose: false
backdropClickToClose: true,
hardwareBackButtonClose: true
}).then(function(modal) {
$scope.warningModal = modal;
$scope.warningModal.show();

View file

@ -1,17 +1,18 @@
'use strict';
angular.module('copayApp.controllers').controller('disclaimerController', function($scope, $timeout, $state, $log, $ionicModal, profileService, uxLanguage, externalLinkService, storageService, $stateParams, startupService) {
angular.module('copayApp.controllers').controller('disclaimerController', function($scope, $timeout, $state, $log, $ionicModal, profileService, uxLanguage, externalLinkService, storageService, $stateParams, startupService, $rootScope) {
$scope.$on("$ionicView.afterEnter", function() {
startupService.ready();
});
$scope.init = function() {
$scope.lang = uxLanguage.currentLanguage;
$scope.terms = {};
$scope.accept1 = $scope.accept2 = $scope.accept3 = false;
$scope.accepted = {};
$scope.accepted.first = $scope.accepted.second = $scope.accepted.third = false;
$scope.backedUp = $stateParams.backedUp;
$scope.resume = $stateParams.resume;
$scope.shrinkView = false;
$timeout(function() {
$scope.$apply();
}, 1);
@ -32,14 +33,9 @@ angular.module('copayApp.controllers').controller('disclaimerController', functi
externalLinkService.open(url, target);
};
$scope.openTermsModal = function() {
$ionicModal.fromTemplateUrl('views/modals/terms.html', {
scope: $scope
}).then(function(modal) {
$scope.termsModal = modal;
$scope.termsModal.show();
});
};
$scope.openTerms = function() {
$scope.shrinkView = !$scope.shrinkView;
}
$scope.goBack = function() {
$state.go('onboarding.backupRequest', {

View file

@ -1,8 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('termsController', function($scope, $log, $state, $window, uxLanguage, profileService, externalLinkService) {
angular.module('copayApp.controllers').controller('termsController', function($scope, $log, $state, $window, uxLanguage, profileService, externalLinkService, gettextCatalog) {
$scope.lang = uxLanguage.currentLanguage;
$scope.disclaimerUrl = $window.appConfig.disclaimerUrl;
$scope.confirm = function() {
profileService.setDisclaimerAccepted(function(err) {
@ -15,7 +14,13 @@ angular.module('copayApp.controllers').controller('termsController', function($s
});
};
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
$scope.openExternalLink = function() {
var url = $window.appConfig.disclaimerUrl;
var optIn = true;
var title = gettextCatalog.getString('View Terms of Service');
var message = gettextCatalog.getString('The official English Terms of Service are available on the BitPay website. Would you like to view them?');
var okText = gettextCatalog.getString('Open Website');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('welcomeController', function($scope, $state, $timeout, $ionicConfig, $log, profileService, startupService) {
angular.module('copayApp.controllers').controller('welcomeController', function($scope, $state, $timeout, $ionicConfig, $log, profileService, startupService, storageService) {
$ionicConfig.views.swipeBackEnabled(false);

View file

@ -1,24 +1,14 @@
angular.module('copayApp.controllers').controller('paperWalletController',
function($scope, $timeout, $log, $ionicModal, $ionicHistory, popupService, gettextCatalog, platformInfo, configService, profileService, $state, bitcore, ongoingProcess, txFormatService, $stateParams, walletService) {
$scope.onQrCodeScanned = function(data) {
$scope.formData.inputData = data;
$scope.onData(data);
};
$scope.onData = function(data) {
$scope.scannedKey = data;
$scope.isPkEncrypted = (data.substring(0, 2) == '6P');
};
function($scope, $timeout, $log, $ionicModal, $ionicHistory, feeService, popupService, gettextCatalog, platformInfo, configService, profileService, $state, bitcore, ongoingProcess, txFormatService, $stateParams, walletService) {
function _scanFunds(cb) {
function getPrivateKey(scannedKey, isPkEncrypted, passphrase, cb) {
if (!isPkEncrypted) return cb(null, scannedKey);
wallet.decryptBIP38PrivateKey(scannedKey, passphrase, null, cb);
$scope.wallet.decryptBIP38PrivateKey(scannedKey, passphrase, null, cb);
};
function getBalance(privateKey, cb) {
wallet.getBalanceFromPrivateKey(privateKey, cb);
$scope.wallet.getBalanceFromPrivateKey(privateKey, cb);
};
function checkPrivateKey(privateKey) {
@ -42,9 +32,6 @@ angular.module('copayApp.controllers').controller('paperWalletController',
};
$scope.scanFunds = function() {
$scope.privateKey = '';
$scope.balanceSat = 0;
ongoingProcess.set('scanning', true);
$timeout(function() {
_scanFunds(function(err, privateKey, balance) {
@ -52,32 +39,40 @@ angular.module('copayApp.controllers').controller('paperWalletController',
if (err) {
$log.error(err);
popupService.showAlert(gettextCatalog.getString('Error scanning funds:'), err || err.toString());
$state.go('tabs.home');
} else {
$scope.privateKey = privateKey;
$scope.balanceSat = balance;
if ($scope.balanceSat <= 0)
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not funds found'));
var config = configService.getSync().wallet.settings;
$scope.balance = txFormatService.formatAmount(balance) + ' ' + config.unitName;
$scope.scanned = true;
}
$scope.$apply();
});
}, 100);
};
function _sweepWallet(cb) {
walletService.getAddress(wallet, true, function(err, destinationAddress) {
walletService.getAddress($scope.wallet, true, function(err, destinationAddress) {
if (err) return cb(err);
wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, tx) {
$scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, testTx) {
if (err) return cb(err);
wallet.broadcastRawTx({
rawTx: tx.serialize(),
network: 'livenet'
}, function(err, txid) {
if (err) return cb(err);
return cb(null, destinationAddress, txid);
var rawTxLength = testTx.serialize().length;
feeService.getCurrentFeeValue('livenet', function(err, feePerKB) {
var opts = {};
opts.fee = Math.round((feePerKB * rawTxLength) / 2000);
$scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, opts, function(err, tx) {
if (err) return cb(err);
$scope.wallet.broadcastRawTx({
rawTx: tx.serialize(),
network: 'livenet'
}, function(err, txid) {
if (err) return cb(err);
return cb(null, destinationAddress, txid);
});
});
});
});
});
@ -95,45 +90,61 @@ angular.module('copayApp.controllers').controller('paperWalletController',
$log.error(err);
popupService.showAlert(gettextCatalog.getString('Error sweeping wallet:'), err || err.toString());
} else {
$scope.openStatusModal('broadcasted', function() {
$ionicHistory.removeBackView();
$state.go('tabs.home');
});
$scope.sendStatus = 'success';
}
$scope.$apply();
});
}, 100);
};
$scope.openStatusModal = function(type, cb) {
$scope.tx = {};
$scope.tx.amountStr = $scope.balance;
$scope.type = type;
$scope.color = wallet.backgroundColor;
$scope.cb = cb;
$ionicModal.fromTemplateUrl('views/modals/tx-status.html', {
scope: $scope
}).then(function(modal) {
$scope.txStatusModal = modal;
$scope.txStatusModal.show();
});
$scope.onSuccessConfirm = function() {
$state.go('tabs.home');
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var wallet = profileService.getWallet($stateParams.walletId);
$scope.$on('Wallet/Changed', function(event, wallet) {
if (!wallet) {
$log.debug('No wallet provided');
return;
}
if (wallet == $scope.wallet) {
$log.debug('No change in wallet');
return;
}
$scope.wallet = wallet;
$scope.needsBackup = wallet.needsBackup;
$scope.walletAlias = wallet.name;
$scope.walletName = wallet.credentials.walletName;
$scope.formData = {};
$scope.formData.inputData = null;
$scope.scannedKey = null;
$scope.balance = null;
$scope.balanceSat = null;
$scope.scanned = false;
$log.debug('Wallet changed: ' + wallet.name);
$timeout(function() {
$scope.$apply();
}, 10);
});
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.scannedKey = (data.stateParams && data.stateParams.privateKey) ? data.stateParams.privateKey : null;
$scope.isPkEncrypted = $scope.scannedKey ? ($scope.scannedKey.substring(0, 2) == '6P') : null;
$scope.sendStatus = null;
$scope.error = false;
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: 'livenet',
});
if (!$scope.wallets || !$scope.wallets.length) {
$scope.noMatchingWallet = true;
return;
}
});
$scope.$on("$ionicView.enter", function(event, data) {
$scope.wallet = $scope.wallets[0];
if (!$scope.wallet) return;
if (!$scope.isPkEncrypted) $scope.scanFunds();
else {
var message = gettextCatalog.getString('Private key encrypted. Enter password');
popupService.showPrompt(null, message, null, function(res) {
$scope.passphrase = res;
$scope.scanFunds();
});
}
});
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesController',
function($scope, $rootScope, $timeout, $log, $stateParams, $ionicHistory, gettextCatalog, configService, profileService, fingerprintService, walletService) {
function($scope, $rootScope, $timeout, $log, $stateParams, $ionicHistory, configService, profileService, fingerprintService, walletService) {
var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.credentials.walletId;
$scope.wallet = wallet;
@ -10,6 +10,16 @@ angular.module('copayApp.controllers').controller('preferencesController',
value: walletService.isEncrypted(wallet)
};
$scope.hiddenBalanceChange = function() {
var opts = {
balance: {
enabled: $scope.hiddenBalance.value
}
};
profileService.toggleHideBalanceFlag(walletId, function(err) {
if (err) $log.error(err);
});
};
$scope.encryptChange = function() {
if (!wallet) return;
@ -75,7 +85,9 @@ angular.module('copayApp.controllers').controller('preferencesController',
var config = configService.getSync();
$scope.hiddenBalance = {
value: $scope.wallet.balanceHidden
};
if (wallet.isPrivKeyExternal)
$scope.externalSource = wallet.getPrivKeyExternalSourceName() == 'ledger' ? 'Ledger' : 'Trezor';

View file

@ -6,9 +6,14 @@ angular.module('copayApp.controllers').controller('preferencesAbout',
$scope.title = gettextCatalog.getString('About') + ' ' + $window.appConfig.nameCase;
$scope.version = $window.version;
$scope.commitHash = $window.commitHash;
$scope.name = $window.appConfig.gitHubRepoName;
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
$scope.openExternalLink = function() {
var url = 'https://github.com/bitpay/' + $window.appConfig.gitHubRepoName + '/tree/' + $window.commitHash + '';
var optIn = true;
var title = gettextCatalog.getString('Open GitHub Project');
var message = gettextCatalog.getString('You can see the latest developments and contribute to this open source app by visiting our project on GitHub.');
var okText = gettextCatalog.getString('Open GitHub');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesAliasController',
function($scope, $timeout, $stateParams, $ionicHistory, gettextCatalog, configService, profileService, walletService) {
function($scope, $timeout, $stateParams, $ionicHistory, configService, profileService, walletService) {
var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.credentials.walletId;
var config = configService.getSync();

View file

@ -1,22 +1,35 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesAltCurrencyController',
function($scope, $log, $timeout, $ionicHistory, gettextCatalog, configService, rateService, lodash, profileService, walletService) {
function($scope, $log, $timeout, $ionicHistory, configService, rateService, lodash, profileService, walletService, storageService) {
var next = 10;
var completeAlternativeList;
var config = configService.getSync();
$scope.currentCurrency = config.wallet.settings.alternativeIsoCode;
$scope.listComplete = false;
function init() {
var unusedCurrencyList = [{
isoCode: 'LTL'
}, {
isoCode: 'BTC'
}];
rateService.whenAvailable(function() {
rateService.whenAvailable(function() {
completeAlternativeList = rateService.listAlternatives();
lodash.remove(completeAlternativeList, function(c) {
return c.isoCode == 'BTC';
$scope.listComplete = false;
var idx = lodash.indexBy(unusedCurrencyList, 'isoCode');
var idx2 = lodash.indexBy($scope.lastUsedAltCurrencyList, 'isoCode');
completeAlternativeList = lodash.reject(rateService.listAlternatives(true), function(c) {
return idx[c.isoCode] || idx2[c.isoCode];
});
$scope.altCurrencyList = completeAlternativeList.slice(0, 10);
$timeout(function() {
$scope.$apply();
});
});
$scope.altCurrencyList = completeAlternativeList.slice(0, next);
});
}
$scope.loadMore = function() {
$timeout(function() {
@ -27,6 +40,17 @@ angular.module('copayApp.controllers').controller('preferencesAltCurrencyControl
}, 100);
};
$scope.findCurrency = function(search) {
if (!search) init();
$scope.altCurrencyList = lodash.filter(completeAlternativeList, function(item) {
var val = item.name;
return lodash.includes(val.toLowerCase(), search.toLowerCase());
});
$timeout(function() {
$scope.$apply();
});
};
$scope.save = function(newAltCurrency) {
var opts = {
wallet: {
@ -41,9 +65,27 @@ angular.module('copayApp.controllers').controller('preferencesAltCurrencyControl
if (err) $log.warn(err);
$ionicHistory.goBack();
saveLastUsed(newAltCurrency);
walletService.updateRemotePreferences(profileService.getWallets(), {}, function() {
$log.debug('Remote preferences saved');
});
});
};
function saveLastUsed(newAltCurrency) {
$scope.lastUsedAltCurrencyList.unshift(newAltCurrency);
$scope.lastUsedAltCurrencyList = lodash.uniq($scope.lastUsedAltCurrencyList, 'isoCode');
$scope.lastUsedAltCurrencyList = $scope.lastUsedAltCurrencyList.slice(0, 3);
storageService.setLastCurrencyUsed(JSON.stringify($scope.lastUsedAltCurrencyList), function() {});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var config = configService.getSync();
$scope.currentCurrency = config.wallet.settings.alternativeIsoCode;
storageService.getLastCurrencyUsed(function(err, lastUsedAltCurrency) {
$scope.lastUsedAltCurrencyList = lastUsedAltCurrency ? JSON.parse(lastUsedAltCurrency) : [];
init();
});
});
});

View file

@ -13,8 +13,11 @@ angular.module('copayApp.controllers').controller('preferencesBitpayCardControll
};
var remove = function(card) {
bitpayCardService.remove(card, function() {
$ionicHistory.removeBackView();
bitpayCardService.remove(card, function(err) {
if (err) {
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not remove card'));
}
$ionicHistory.clearHistory();
$timeout(function() {
$state.go('tabs.home');
}, 100);

View file

@ -1,14 +1,14 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
function($scope, $log, $stateParams, configService, applicationService, profileService, storageService) {
function($scope, $log, $stateParams, configService, applicationService, profileService, storageService, $window) {
$scope.success = null;
var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.credentials.walletId;
var defaults = configService.getDefaults();
var config = configService.getSync();
$scope.appName = $window.appConfig.nameCase;
$scope.bwsurl = {
value: (config.bwsFor && config.bwsFor[walletId]) || defaults.bws.url
};

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesColorController', function($scope, $timeout, $log, $stateParams, $ionicHistory, gettextCatalog, configService, profileService) {
angular.module('copayApp.controllers').controller('preferencesColorController', function($scope, $timeout, $log, $stateParams, $ionicHistory, configService, profileService) {
var wallet = profileService.getWallet($stateParams.walletId);
$scope.wallet = wallet;
var walletId = wallet.credentials.walletId;

View file

@ -1,10 +1,14 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesFeeController', function($scope, $timeout, $ionicHistory, gettextCatalog, configService, feeService, ongoingProcess) {
angular.module('copayApp.controllers').controller('preferencesFeeController', function($scope, $timeout, $ionicHistory, gettextCatalog, configService, feeService, ongoingProcess, popupService) {
ongoingProcess.set('gettingFeeLevels', true);
feeService.getFeeLevels(function(levels) {
feeService.getFeeLevels(function(err, levels) {
ongoingProcess.set('gettingFeeLevels', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
$scope.feeLevels = levels;
$scope.$apply();
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesGlideraController',
function($scope, $log, $timeout, $state, ongoingProcess, glideraService, popupService, gettextCatalog) {
function($scope, $log, $timeout, $state, $ionicHistory, ongoingProcess, glideraService, popupService, gettextCatalog) {
$scope.update = function(opts) {
if (!$scope.token || !$scope.permissions) return;
@ -41,8 +41,9 @@ angular.module('copayApp.controllers').controller('preferencesGlideraController'
popupService.showConfirm('Glidera', 'Are you sure you would like to log out of your Glidera account?', null, null, function(res) {
if (res) {
glideraService.removeToken(function() {
$ionicHistory.clearHistory();
$timeout(function() {
$state.go('tabs.buyandsell.glidera');
$state.go('tabs.home');
}, 100);
});
}
@ -52,14 +53,6 @@ angular.module('copayApp.controllers').controller('preferencesGlideraController'
$scope.$on("$ionicView.enter", function(event, data){
$scope.network = glideraService.getEnvironment();
$scope.token = null;
$scope.permissions = null;
$scope.email = null;
$scope.personalInfo = null;
$scope.txs = null;
$scope.status = null;
$scope.limits = null;
ongoingProcess.set('connectingGlidera', true);
glideraService.init($scope.token, function(err, glidera) {
ongoingProcess.set('connectingGlidera');

View file

@ -1,10 +1,11 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesHistory',
function($scope, $log, $stateParams, $timeout, $state, $ionicHistory, gettextCatalog, storageService, platformInfo, profileService, lodash) {
function($scope, $log, $stateParams, $timeout, $state, $ionicHistory, storageService, platformInfo, profileService, lodash, $window) {
$scope.wallet = profileService.getWallet($stateParams.walletId);
$scope.csvReady = false;
$scope.isCordova = platformInfo.isCordova;
$scope.appName = $window.appConfig.nameCase;
$scope.csvHistory = function(cb) {
var allTxs = [];
@ -31,8 +32,7 @@ angular.module('copayApp.controllers').controller('preferencesHistory',
if (err) {
$log.warn('Failed to generate CSV:', err);
$scope.err = err;
}
else {
} else {
$log.warn('Failed to generate CSV: no transactions');
$scope.err = 'no transactions';
}
@ -45,7 +45,7 @@ angular.module('copayApp.controllers').controller('preferencesHistory',
var data = txs;
var satToBtc = 1 / 100000000;
$scope.csvContent = [];
$scope.csvFilename = 'Copay-' + $scope.wallet.name + '.csv';
$scope.csvFilename = $scope.appName + '-' + $scope.wallet.name + '.csv';
$scope.csvHeader = ['Date', 'Destination', 'Description', 'Amount', 'Currency', 'Txid', 'Creator', 'Copayers', 'Comment'];
var _amount, _note, _copayers, _creator, _comment;

View file

@ -1,62 +1,15 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesInformation',
function($scope, $log, $timeout, $ionicHistory, $ionicScrollDelegate, platformInfo, gettextCatalog, lodash, profileService, configService, $stateParams, walletService, $state) {
var base = 'xpub';
function($scope, $log, $ionicHistory, platformInfo, lodash, profileService, configService, $stateParams, $state) {
var wallet = profileService.getWallet($stateParams.walletId);
var walletId = wallet.id;
var config = configService.getSync();
var b = 1;
var colorCounter = 1;
var BLACK_WALLET_COLOR = '#202020';
$scope.isCordova = platformInfo.isCordova;
config.colorFor = config.colorFor || {};
$scope.sendAddrs = function() {
function formatDate(ts) {
var dateObj = new Date(ts * 1000);
if (!dateObj) {
$log.debug('Error formating a date');
return 'DateError';
}
if (!dateObj.toJSON()) {
return '';
}
return dateObj.toJSON();
};
$timeout(function() {
wallet.getMainAddresses({
doNotVerify: true
}, function(err, addrs) {
if (err) {
$log.warn(err);
return;
};
var body = 'Copay Wallet "' + $scope.walletName + '" Addresses\n Only Main Addresses are shown.\n\n';
body += "\n";
body += addrs.map(function(v) {
return ('* ' + v.address + ' ' + base + v.path.substring(1) + ' ' + formatDate(v.createdOn));
}).join("\n");
window.plugins.socialsharing.shareViaEmail(
body,
'Copay Addresses',
null, // TO: must be null or an array
null, // CC: must be null or an array
null, // BCC: must be null or an array
null, // FILES: can be null, a string, or an array
function() {},
function() {}
);
$timeout(function() {
$scope.$apply();
}, 1000);
});
}, 100);
};
$scope.saveBlack = function() {
function save(color) {
var opts = {
@ -71,14 +24,8 @@ angular.module('copayApp.controllers').controller('preferencesInformation',
});
};
if (b != 5) return b++;
save('#202020');
};
$scope.scan = function() {
walletService.startScan(wallet);
$ionicHistory.removeBackView();
$state.go('tabs.home');
if (colorCounter != 5) return colorCounter++;
save(BLACK_WALLET_COLOR);
};
$scope.$on("$ionicView.enter", function(event, data) {
@ -95,29 +42,5 @@ angular.module('copayApp.controllers').controller('preferencesInformation',
$scope.M = c.m;
$scope.N = c.n;
$scope.pubKeys = lodash.pluck(c.publicKeyRing, 'xPubKey');
$scope.addrs = null;
wallet.getMainAddresses({
doNotVerify: true
}, function(err, addrs) {
if (err) {
$log.warn(err);
return;
};
var last10 = [],
i = 0,
e = addrs.pop();
while (i++ < 10 && e) {
e.path = base + e.path.substring(1);
last10.push(e);
e = addrs.pop();
}
$scope.addrs = last10;
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
});
});
});

View file

@ -1,12 +1,18 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesLanguageController',
function($scope, $log, $ionicHistory, gettextCatalog, configService, profileService, uxLanguage, walletService, externalLinkService) {
function($scope, $log, $ionicHistory, configService, profileService, uxLanguage, walletService, externalLinkService, gettextCatalog) {
$scope.availableLanguages = uxLanguage.getLanguages();
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
$scope.openExternalLink = function() {
var url = 'https://crowdin.com/project/copay';
var optIn = true;
var title = gettextCatalog.getString('Open Translation Community');
var message = gettextCatalog.getString('You can make contributions by signing up on our Crowdin community translation website. Were looking forward to hearing from you!');
var okText = gettextCatalog.getString('Open Crowdin');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.save = function(newLang) {
@ -18,19 +24,18 @@ angular.module('copayApp.controllers').controller('preferencesLanguageController
}
};
uxLanguage._set(newLang);
configService.set(opts, function(err) {
if (err) $log.warn(err);
$ionicHistory.goBack();
uxLanguage.init(function() {
walletService.updateRemotePreferences(profileService.getWallets(), {}, function() {
$log.debug('Remote preferences saved');
});
walletService.updateRemotePreferences(profileService.getWallets(), {}, function() {
$log.debug('Remote preferences saved');
});
});
$ionicHistory.goBack();
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.currentLanguage = uxLanguage.getCurrentLanguage();
});
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesLogs',
function($scope, historicLog, gettextCatalog) {
function($scope, historicLog) {
$scope.$on("$ionicView.enter", function(event, data) {
$scope.logs = historicLog.get();

View file

@ -1,24 +1,34 @@
'use strict';
angular.module('copayApp.controllers').controller('proposalsController',
function($timeout, $scope, profileService, $log, txpModalService) {
function($timeout, $scope, profileService, $log, txpModalService, addressbookService) {
$scope.fetchingProposals = true;
$scope.$on("$ionicView.enter", function(event, data){
profileService.getTxps(50, function(err, txps) {
$scope.fetchingProposals = false;
if (err) {
$log.error(err);
return;
}
$scope.txps = txps;
$timeout(function() {
$scope.$apply();
}, 1);
$scope.$on("$ionicView.enter", function(event, data) {
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};
profileService.getTxps(50, function(err, txps) {
$scope.fetchingProposals = false;
if (err) {
$log.error(err);
return;
}
$scope.txps = txps;
$timeout(function() {
$scope.$apply();
});
});
});
});
$scope.openTxpModal = txpModalService.open;
$scope.createdWithinPastDay = function(time) {
var now = new Date();
var date = new Date(time * 1000);
return (now.getTime() - date.getTime()) < (1000 * 60 * 60 * 24);
};
});

View file

@ -1,219 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('sellGlideraController',
function($scope, $timeout, $log, profileService, glideraService, bwcError, lodash, walletService, configService, ongoingProcess, popupService, gettextCatalog) {
var self = this;
this.data = {};
this.show2faCodeInput = null;
this.success = null;
var wallet;
$scope.network = glideraService.getEnvironment();
$scope.$on('Wallet/Changed', function(event, w) {
if (lodash.isEmpty(w)) {
$log.debug('No wallet provided');
return;
}
wallet = w;
$log.debug('Wallet changed: ' + w.name);
});
$scope.update = function(opts) {
if (!$scope.token || !$scope.permissions) return;
$log.debug('Updating Glidera Account...');
var accessToken = $scope.token;
var permissions = $scope.permissions;
opts = opts || {};
glideraService.getStatus(accessToken, function(err, data) {
$scope.status = data;
});
glideraService.getLimits(accessToken, function(err, limits) {
$scope.limits = limits;
});
if (permissions.transaction_history) {
glideraService.getTransactions(accessToken, function(err, data) {
$scope.txs = data;
});
}
if (permissions.view_email_address && opts.fullUpdate) {
glideraService.getEmail(accessToken, function(err, data) {
$scope.email = data.email;
});
}
if (permissions.personal_info && opts.fullUpdate) {
glideraService.getPersonalInfo(accessToken, function(err, data) {
$scope.personalInfo = data;
});
}
};
this.getSellPrice = function(token, price) {
var self = this;
if (!price || (price && !price.qty && !price.fiat)) {
self.sellPrice = null;
return;
}
self.gettingSellPrice = true;
glideraService.sellPrice(token, price, function(err, sellPrice) {
self.gettingSellPrice = false;
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get exchange information. Please, try again'));
return;
}
self.sellPrice = sellPrice;
});
};
this.get2faCode = function(token) {
var self = this;
ongoingProcess.set('Sending 2FA code...', true);
$timeout(function() {
glideraService.get2faCode(token, function(err, sent) {
ongoingProcess.set('Sending 2FA code...', false);
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not send confirmation code to your phone'));
} else {
self.show2faCodeInput = sent;
}
});
}, 100);
};
this.createTx = function(token, permissions, twoFaCode) {
var self = this;
var outputs = [];
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
if (!wallet) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('No wallet selected'));
return;
}
ongoingProcess.set('creatingTx', true);
walletService.getAddress(wallet, null, function(err, refundAddress) {
if (!refundAddress) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), bwcError.msg(err, 'Could not create address'));
return;
}
glideraService.getSellAddress(token, function(err, sellAddress) {
if (!sellAddress || err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Could not get the destination bitcoin address'));
return;
}
var amount = parseInt((self.sellPrice.qty * 100000000).toFixed(0));
var comment = 'Glidera transaction';
outputs.push({
'toAddress': sellAddress,
'amount': amount,
'message': comment
});
var txp = {
toAddress: sellAddress,
amount: amount,
outputs: outputs,
message: comment,
payProUrl: null,
excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
feeLevel: walletSettings.feeLevel || 'normal',
customData: {
'glideraToken': token
}
};
walletService.createTx(wallet, txp, function(err, createdTxp) {
ongoingProcess.clear();
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
walletService.prepare(wallet, function(err, password) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
ongoingProcess.set('signingTx', true);
walletService.publishTx(wallet, createdTxp, function(err, publishedTxp) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
walletService.signTx(wallet, publishedTxp, password, function(err, signedTxp) {
if (err) {
ongoingProcess.clear();
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
walletService.removeTx(wallet, signedTxp, function(err) {
if (err) $log.debug(err);
});
return;
}
var rawTx = signedTxp.raw;
var data = {
refundAddress: refundAddress,
signedTransaction: rawTx,
priceUuid: self.sellPrice.priceUuid,
useCurrentPrice: self.sellPrice.priceUuid ? false : true,
ip: null
};
ongoingProcess.set('Selling Bitcoin', true);
glideraService.sell(token, twoFaCode, data, function(err, data) {
ongoingProcess.clear();
if (err) {
popupService.showAlert(gettextCatalog.getString('Error'), err.message || bwcError.msg(err));
return;
}
self.success = data;
$timeout(function() {
$scope.$digest();
});
});
});
});
});
});
});
});
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.token = null;
$scope.permissions = null;
$scope.email = null;
$scope.personalInfo = null;
$scope.txs = null;
$scope.status = null;
$scope.limits = null;
ongoingProcess.set('connectingGlidera', true);
glideraService.init($scope.token, function(err, glidera) {
ongoingProcess.set('connectingGlidera');
if (err || !glidera) {
if (err) popupService.showAlert(gettextCatalog.getString('Error'), err);
return;
}
$scope.token = glidera.token;
$scope.permissions = glidera.permissions;
$scope.update({fullUpdate: true});
});
$scope.wallets = profileService.getWallets({
network: $scope.network,
n: 1,
onlyComplete: true
});
});
});

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,29 +13,132 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isNW = platformInfo.isNW;
$scope.showRateCard = {};
$scope.$on("$ionicView.afterEnter", function() {
startupService.ready();
});
if (!$scope.homeTip) {
storageService.getHomeTipAccepted(function(error, value) {
$scope.homeTip = (value == 'accepted') ? false : true;
});
}
$scope.$on("$ionicView.beforeEnter", function(event, data) {
if (!$scope.homeTip) {
storageService.getHomeTipAccepted(function(error, value) {
$scope.homeTip = (value == 'accepted') ? false : true;
});
}
if ($scope.isNW) {
latestReleaseService.checkLatestRelease(function(err, newRelease) {
if (err) {
$log.warn(err);
return;
if ($scope.isNW) {
latestReleaseService.checkLatestRelease(function(err, newRelease) {
if (err) {
$log.warn(err);
return;
}
if (newRelease) $scope.newRelease = true;
});
}
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 * 7 * 60 * 60;
$scope.showRateCard.value = timeExceeded && !feedbackInfo.sent;
$timeout(function() {
$scope.$apply();
});
}
if (newRelease) $scope.newRelease = true;
});
}
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
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) {
updateAllWallets();
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};
});
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId, type, n) {
var wallet = profileService.getWallet(walletId);
updateWallet(wallet);
if ($scope.recentTransactionsEnabled) getNotifications();
}),
$rootScope.$on('Local/TxAction', function(e, walletId) {
$log.debug('Got action for wallet ' + walletId);
var wallet = profileService.getWallet(walletId);
updateWallet(wallet);
if ($scope.recentTransactionsEnabled) getNotifications();
})
];
configService.whenAvailable(function() {
nextStep(function() {
var config = configService.getSync();
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
$scope.amazonEnabled = config.amazon.enabled;
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled);
var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled;
var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled;
$scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled;
$scope.recentTransactionsEnabled = config.recentTransactions.enabled;
if ($scope.recentTransactionsEnabled) getNotifications();
if ($scope.bitpayCardEnabled) bitpayCardCache();
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
});
});
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
$scope.createdWithinPastDay = function(time) {
var now = new Date();
var date = new Date(time * 1000);
return (now.getTime() - date.getTime()) < (1000 * 60 * 60 * 24);
};
$scope.openExternalLink = function() {
var url = 'https://github.com/bitpay/copay/releases/latest';
var optIn = true;
var title = gettextCatalog.getString('Update Available');
var message = gettextCatalog.getString('An update to this app is available. For your security, please update to the latest version.');
var okText = gettextCatalog.getString('View Update');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
@ -43,7 +146,10 @@ angular.module('copayApp.controllers').controller('tabHomeController',
wallet = profileService.getWallet(n.walletId);
if (n.txid) {
openTxModal(n);
$state.transitionTo('tabs.wallet.tx-details', {
txid: n.txid,
walletId: n.walletId
});
} else {
var txp = lodash.find($scope.txps, {
id: n.txpId
@ -65,37 +171,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}
};
var openTxModal = function(n) {
wallet = profileService.getWallet(n.walletId);
ongoingProcess.set('loadingTxInfo', true);
walletService.getTx(wallet, n.txid, function(err, tx) {
ongoingProcess.set('loadingTxInfo', false);
if (err) {
$log.error(err);
return popupService.showAlert(gettextCatalog.getString('Error'), err);
}
if (!tx) {
$log.warn('No tx found');
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
}
$scope.wallet = wallet;
$scope.btx = lodash.cloneDeep(tx);
$state.transitionTo('tabs.wallet.tx-details', {
txid: $scope.btx.txid,
walletId: $scope.walletId
});
walletService.getTxNote(wallet, n.txid, function(err, note) {
if (err) $log.warn('Could not fetch transaction note: ' + err);
$scope.btx.note = note;
});
});
};
$scope.openWallet = function(wallet) {
if (!wallet.isComplete()) {
return $state.go('tabs.copayers', {
@ -117,7 +192,8 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$scope.txpsN = n;
$timeout(function() {
$ionicScrollDelegate.resize();
}, 100);
$scope.$apply();
}, 10);
})
};
@ -132,8 +208,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) {
@ -141,25 +220,6 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}
});
});
if (!$scope.recentTransactionsEnabled) return;
$scope.fetchingNotifications = true;
profileService.getNotifications({
limit: 3
}, function(err, n) {
if (err) {
$log.error(err);
return;
}
$scope.fetchingNotifications = false;
$scope.notifications = n;
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 100);
})
};
var updateWallet = function(wallet) {
@ -171,20 +231,22 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}
wallet.status = status;
updateTxps();
});
};
if (!$scope.recentTransactionsEnabled) return;
$scope.fetchingNotifications = true;
profileService.getNotifications({
limit: 3
}, function(err, notifications) {
$scope.fetchingNotifications = false;
if (err) {
$log.error(err);
return;
}
$scope.notifications = notifications;
});
var getNotifications = function() {
profileService.getNotifications({
limit: 3
}, function(err, n) {
if (err) {
$log.error(err);
return;
}
$scope.notifications = n;
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
});
};
@ -202,7 +264,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();
});
});
@ -213,7 +275,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 100);
}, 10);
};
var bitpayCardCache = function() {
@ -241,57 +303,4 @@ angular.module('copayApp.controllers').controller('tabHomeController',
}, 300);
updateAllWallets();
};
$scope.$on("$ionicView.enter", function(event, data) {
updateAllWallets();
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};
});
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId, type, n) {
var wallet = profileService.getWallet(walletId);
updateWallet(wallet);
}),
$rootScope.$on('Local/TxAction', function(e, walletId) {
$log.debug('Got action for wallet ' + walletId);
var wallet = profileService.getWallet(walletId);
updateWallet(wallet);
})
];
configService.whenAvailable(function() {
nextStep(function() {
var config = configService.getSync();
var isWindowsPhoneApp = platformInfo.isWP && platformInfo.isCordova;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
$scope.coinbaseEnabled = config.coinbase.enabled && !isWindowsPhoneApp;
$scope.amazonEnabled = config.amazon.enabled;
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
var buyAndSellEnabled = !$scope.externalServices.BuyAndSell && ($scope.glideraEnabled || $scope.coinbaseEnabled);
var amazonEnabled = !$scope.externalServices.AmazonGiftCards && $scope.amazonEnabled;
var bitpayCardEnabled = !$scope.externalServices.BitpayCard && $scope.bitpayCardEnabled;
$scope.nextStepEnabled = buyAndSellEnabled || amazonEnabled || bitpayCardEnabled;
$scope.recentTransactionsEnabled = config.recentTransactions.enabled;
if ($scope.bitpayCardEnabled) bitpayCardCache();
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
}, 10);
});
});
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
});

View file

@ -1,9 +1,12 @@
'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, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError) {
var listeners = [];
var MENU_ITEM_HEIGHT = 55;
$scope.isCordova = platformInfo.isCordova;
$scope.isNW = platformInfo.isNW;
$scope.walletAddrs = {};
$scope.shareAddress = function(addr) {
if ($scope.generatingAddress) return;
@ -18,14 +21,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({
@ -39,6 +49,12 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
}, 100);
};
$scope.showAddresses = function() {
$state.transitionTo('tabs.receive.addresses', {
walletId: $scope.wallet.credentials.walletId
});
};
$scope.openBackupNeededModal = function() {
$ionicModal.fromTemplateUrl('views/includes/backupNeededPopup.html', {
scope: $scope,
@ -67,6 +83,24 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
});
};
$scope.setWallet = function(index) {
$scope.wallet = $scope.wallets[index];
$scope.walletIndex = index;
if ($scope.walletAddrs[$scope.wallet.id].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 +111,82 @@ 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);
});
};
var goRequestAmount = function() {
$scope.menu.hide();
$state.go('tabs.receive.amount', {
customAmount: true,
toAddress: $scope.addr
});
}
$scope.showMenu = function(allAddresses, $event) {
var requestAmountObj = {
text: gettextCatalog.getString('Request Specific amount'),
action: goRequestAmount,
};
$scope.items = [requestAmountObj];
$scope.height = $scope.items.length * MENU_ITEM_HEIGHT;
$ionicPopover.fromTemplateUrl('views/includes/menu-popover.html', {
scope: $scope
}).then(function(popover) {
$scope.menu = popover;
$scope.menu.show($event);
});
};
$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

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, $rootScope) {
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService) {
var originalList;
var CONTACTS_SHOW_LIMIT;
@ -55,7 +55,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$timeout(function() {
$ionicScrollDelegate.resize();
$scope.$apply();
});
}, 10);
});
};
@ -128,10 +128,12 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
});
}
$scope.checkingBalance = true;
var index = 0;
lodash.each(wallets, function(w) {
walletService.getStatus(w, {}, function(err, status) {
++index;
if (index == wallets.length) $scope.checkingBalance = false;
if (err || !status) {
$log.error(err);
return;
@ -143,15 +145,15 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
}
if (index == wallets.length) {
if ($scope.hasFunds != true) {
$ionicScrollDelegate.freezeScroll(true);
}
$timeout(function() {
$scope.$apply();
});
}
});
});
if ($scope.hasFunds != true) {
$ionicScrollDelegate.freezeScroll(true);
}
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {

View file

@ -1,30 +1,58 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, $window, uxLanguage, platformInfo, profileService, feeService, configService, externalLinkService) {
angular.module('copayApp.controllers').controller('tabSettingsController', function($scope, $window, $ionicModal, $log, lodash, uxLanguage, platformInfo, profileService, feeService, configService, externalLinkService, bitpayCardService, storageService, glideraService, gettextCatalog) {
var updateConfig = function() {
var config = configService.getSync();
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
var isWindowsPhoneApp = platformInfo.isWP && isCordova;
$scope.usePushNotifications = isCordova && !isWP;
$scope.isCordova = isCordova;
$scope.appName = $window.appConfig.nameCase;
$scope.unitName = config.wallet.settings.unitName;
$scope.currentLanguageName = uxLanguage.getCurrentLanguageName();
$scope.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
};
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = feeService.getCurrentFeeLevel();
$scope.wallets = profileService.getWallets();
configService.whenAvailable(function(config) {
$scope.unitName = config.wallet.settings.unitName;
$scope.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
};
$scope.bitpayCardEnabled = config.bitpayCard.enabled;
$scope.glideraEnabled = config.glidera.enabled && !isWindowsPhoneApp;
if ($scope.bitpayCardEnabled) {
bitpayCardService.getBitpayDebitCards(function(err, data) {
if (err) $log.error(err);
if (!lodash.isEmpty(data)) {
$scope.bitpayCards = true;
}
});
}
if ($scope.glideraEnabled) {
storageService.getGlideraToken(glideraService.getEnvironment(), function(err, token) {
if (err) $log.error(err);
$scope.glideraToken = token;
});
}
});
};
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
$scope.openExternalLink = function() {
var url = 'https://help.bitpay.com/bitpay-app';
var optIn = true;
var title = gettextCatalog.getString('BitPay Help Center');
var message = gettextCatalog.getString('Help and support information is available at the BitPay Help Center website. Would you like to go there now?');
var okText = gettextCatalog.getString('Open Help Center');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, incomingData, lodash, popupService) {
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, incomingData, lodash, popupService, gettextCatalog) {
$scope.onScan = function(data) {
if (!incomingData.redir(data)) {

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('termOfUseController',
function($scope, $window, uxLanguage, gettextCatalog, externalLinkService) {
function($scope, $window, uxLanguage, externalLinkService) {
$scope.lang = uxLanguage.currentLanguage;
$scope.disclaimerUrl = $window.appConfig.disclaimerUrl;

View file

@ -1,8 +1,14 @@
'use strict';
angular.module('copayApp.controllers').controller('translatorsController',
function($scope, externalLinkService) {
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
function($scope, externalLinkService, gettextCatalog) {
$scope.openExternalLink = function() {
var url = 'https://crowdin.com/project/copay';
var optIn = true;
var title = gettextCatalog.getString('Open Translation Community');
var message = gettextCatalog.getString('You can make contributions by signing up on our Crowdin community translation website. Were looking forward to hearing from you!');
var okText = gettextCatalog.getString('Open Crowdin');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
});

View file

@ -1,27 +1,23 @@
'use strict';
angular.module('copayApp.controllers').controller('txDetailsController', function($log, $timeout, $ionicHistory, $scope, $filter, $stateParams, ongoingProcess, walletService, lodash, gettextCatalog, profileService, configService, txFormatService, externalLinkService, popupService) {
var config = configService.getSync();
var configWallet = config.wallet;
var walletSettings = configWallet.settings;
var wallet = profileService.getWallet($stateParams.walletId);
angular.module('copayApp.controllers').controller('txDetailsController', function($log, $ionicHistory, $scope, $timeout, walletService, lodash, gettextCatalog, profileService, configService, externalLinkService, popupService, ongoingProcess) {
$scope.wallet = wallet;
$scope.title = gettextCatalog.getString('Transaction');
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.title = gettextCatalog.getString('Transaction');
$scope.wallet = profileService.getWallet(data.stateParams.walletId);
$scope.color = $scope.wallet.color;
$scope.copayerId = $scope.wallet.credentials.copayerId;
$scope.isShared = $scope.wallet.credentials.n > 1;
$scope.init = function() {
$scope.alternativeIsoCode = walletSettings.alternativeIsoCode;
$scope.color = wallet.color;
$scope.copayerId = wallet.credentials.copayerId;
$scope.isShared = wallet.credentials.n > 1;
walletService.getTx(wallet, $stateParams.txid, function(err, tx) {
ongoingProcess.set('loadingTxInfo', true);
walletService.getTx($scope.wallet, data.stateParams.txid, function(err, tx) {
ongoingProcess.set('loadingTxInfo', false);
if (err) {
$log.warn('Could not get tx');
$ionicHistory.goBack();
return;
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not found'));
}
$scope.btx = tx;
$scope.btx.feeLevel = walletSettings.feeLevel;
if ($scope.btx.action != 'invalid') {
if ($scope.btx.action == 'sent') $scope.title = gettextCatalog.getString('Sent Funds');
if ($scope.btx.action == 'received') $scope.title = gettextCatalog.getString('Received Funds');
@ -33,8 +29,11 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
updateMemo();
initActionList();
$timeout(function() {
$scope.$apply();
});
});
};
});
function getDisplayAmount(amountStr) {
return amountStr.split(' ')[0];
@ -45,7 +44,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
}
function updateMemo() {
walletService.getTxNote(wallet, $scope.btx.txid, function(err, note) {
walletService.getTxNote($scope.wallet, $scope.btx.txid, function(err, note) {
if (err) {
$log.warn('Could not fetch transaction note: ' + err);
return;
@ -53,18 +52,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
if (!note) return;
$scope.btx.note = note;
walletService.getTx(wallet, $scope.btx.txid, function(err, tx) {
if (err) {
$log.error(err);
return;
}
tx.note = note;
$timeout(function() {
$scope.$apply();
});
});
$scope.$apply();
});
}
@ -109,9 +97,12 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
}
if ($scope.btx.note && $scope.btx.note.body) opts.defaultText = $scope.btx.note.body;
popupService.showPrompt(wallet.name, gettextCatalog.getString('Memo'), opts, function(text) {
popupService.showPrompt($scope.wallet.name, gettextCatalog.getString('Memo'), opts, function(text) {
if (typeof text == "undefined") return;
$scope.btx.note = {
body: text
};
$log.debug('Saving memo');
var args = {
@ -119,17 +110,10 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
body: text
};
walletService.editTxNote(wallet, args, function(err, res) {
walletService.editTxNote($scope.wallet, args, function(err, res) {
if (err) {
$log.debug('Could not save tx comment ' + err);
return;
}
// This is only to refresh the current screen data
updateMemo();
$scope.btx.searcheableString = null;
$timeout(function() {
$scope.$apply();
});
});
});
};
@ -137,23 +121,42 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.viewOnBlockchain = function() {
var btx = $scope.btx;
var url = 'https://' + ($scope.getShortNetworkName() == 'test' ? 'test-' : '') + 'insight.bitpay.com/tx/' + btx.txid;
var title = 'View Transaction on Insight';
var message = 'Would you like to view this transaction on the Insight blockchain explorer?';
$scope.openExternalLink(url, true, title, message, 'Open Insight', 'Go back');
};
$scope.openExternalLink = function(url, optIn, title, message, okText, cancelText) {
var optIn = true;
var title = gettextCatalog.getString('View Transaction on Insight');
var message = gettextCatalog.getString('Would you like to view this transaction on the Insight blockchain explorer?');
var okText = gettextCatalog.getString('Open Insight');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.getShortNetworkName = function() {
var n = wallet.credentials.network;
var n = $scope.wallet.credentials.network;
return n.substring(0, 4);
};
$scope.getFiatRate = function() {
if ($scope.rateDate) return;
var alternativeIsoCode = $scope.wallet.status.alternativeIsoCode;
$scope.loadingRate = true;
$scope.wallet.getFiatRate({
code: alternativeIsoCode,
ts: $scope.btx.time * 1000
}, function(err, res) {
$scope.loadingRate = false;
if (err) {
$log.debug('Could not get historic rate');
return;
}
if (res && res.rate) {
$scope.rateDate = res.fetchedOn;
$scope.rateStr = res.rate + ' ' + alternativeIsoCode;
$scope.$apply();
}
});
};
$scope.cancel = function() {
$scope.txDetailsModal.hide();
};
$scope.init();
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, profileService, lodash, configService, gettextCatalog, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService) {
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog) {
var HISTORY_SHOW_LIMIT = 10;
var currentTxHistoryPage = 0;
@ -10,6 +10,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
$scope.openTxpModal = txpModalService.open;
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isIOS = platformInfo.isIOS;
$scope.amountIsCollapsible = !$scope.isAndroid;
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
@ -46,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, {
@ -57,18 +60,17 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (err === 'WALLET_NOT_REGISTERED') {
$scope.walletNotRegistered = true;
} else {
$scope.updateStatusError = true;
$scope.updateStatusError = bwcError.msg(err, gettextCatalog.getString('BWS Error'));
}
$scope.status = null;
return;
} else {
setPendingTxps(status.pendingTxps);
$scope.status = status;
}
setPendingTxps(status.pendingTxps);
$scope.status = status;
refreshAmountSection();
$timeout(function() {
$scope.$apply();
}, 1);
});
});
};
@ -87,6 +89,14 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
$scope.close = function() {
$scope.searchModal.hide();
};
$scope.openTx = function(tx) {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$scope.searchModal.hide();
$scope.openTxModal(tx);
};
};
$scope.openTxModal = function(btx) {
@ -98,11 +108,25 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
});
};
$scope.openBalanceModal = function() {
$ionicModal.fromTemplateUrl('views/modals/wallet-balance.html', {
scope: $scope
}).then(function(modal) {
$scope.walletBalanceModal = modal;
$scope.walletBalanceModal.show();
});
$scope.close = function() {
$scope.walletBalanceModal.hide();
};
};
$scope.recreate = function() {
walletService.recreate($scope.wallet, function(err) {
if (err) return;
$timeout(function() {
walletService.startScan($scope.wallet, function() {
$scope.updateAll();
$scope.$apply();
});
});
@ -151,6 +175,52 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
}
};
$scope.getDate = function(txCreated) {
var date = new Date(txCreated * 1000);
return date;
};
$scope.isFirstInGroup = function(index) {
if (index === 0) {
return true;
}
var curTx = $scope.txHistory[index];
var prevTx = $scope.txHistory[index - 1];
return !createdDuringSameMonth(curTx, prevTx);
};
$scope.isLastInGroup = function(index) {
if (index === $scope.txHistory.length - 1) {
return true;
}
return $scope.isFirstInGroup(index + 1);
};
function createdDuringSameMonth(tx1, tx2) {
var date1 = new Date(tx1.time * 1000);
var date2 = new Date(tx2.time * 1000);
return getMonthYear(date1) === getMonthYear(date2);
}
$scope.createdWithinPastDay = function(time) {
var now = new Date();
var date = new Date(time * 1000);
return (now.getTime() - date.getTime()) < (1000 * 60 * 60 * 24);
};
$scope.isDateInCurrentMonth = function(date) {
var now = new Date();
return getMonthYear(now) === getMonthYear(date);
};
function getMonthYear(date) {
return date.getMonth() + date.getFullYear();
}
$scope.isUnconfirmed = function(tx) {
return !tx.confirmations || tx.confirmations === 0;
};
$scope.showMore = function() {
$timeout(function() {
currentTxHistoryPage++;
@ -177,9 +247,99 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var prevPos;
$scope.wallet = profileService.getWallet(data.stateParams.walletId);
function getScrollPosition() {
var scrollPosition = $ionicScrollDelegate.getScrollPosition();
if (!scrollPosition) {
$window.requestAnimationFrame(function() {
getScrollPosition();
});
return;
}
var pos = scrollPosition.top;
if (pos === prevPos) {
$window.requestAnimationFrame(function() {
getScrollPosition();
});
return;
}
prevPos = pos;
refreshAmountSection(pos);
};
function refreshAmountSection(scrollPos) {
$scope.showBalanceButton = false;
if ($scope.wallet.status) {
$scope.showBalanceButton = ($scope.wallet.status.totalBalanceSat != $scope.wallet.status.spendableAmount);
}
if (!$scope.amountIsCollapsible) {
var t = ($scope.showBalanceButton ? 15 : 45);
$scope.amountScale = 'translateY(' + t + 'px)';
return;
}
scrollPos = scrollPos || 0;
var amountHeight = 210 - scrollPos;
if (amountHeight < 80) {
amountHeight = 80;
}
var contentMargin = amountHeight;
if (contentMargin > 210) {
contentMargin = 210;
}
var amountScale = (amountHeight / 210);
if (amountScale < 0.5) {
amountScale = 0.5;
}
if (amountScale > 1.1) {
amountScale = 1.1;
}
var s = amountScale;
// Make space for the balance button when it needs to display.
var TOP_NO_BALANCE_BUTTON = 115;
var TOP_BALANCE_BUTTON = 30;
var top = TOP_NO_BALANCE_BUTTON;
if ($scope.showBalanceButton) {
top = TOP_BALANCE_BUTTON;
}
var amountTop = ((amountScale - 0.7) / 0.7) * top;
if (amountTop < -10) {
amountTop = -10;
}
if (amountTop > top) {
amountTop = top;
}
var t = amountTop;
$scope.altAmountOpacity = (amountHeight - 100) / 80;
$window.requestAnimationFrame(function() {
$scope.amountHeight = amountHeight + 'px';
$scope.contentMargin = contentMargin + 'px';
$scope.amountScale = 'scale3d(' + s + ',' + s + ',' + s + ') translateY(' + t + 'px)';
$scope.$digest();
getScrollPosition();
});
}
var scrollWatcherInitialized;
$scope.$on("$ionicView.enter", function(event, data) {
if ($scope.isCordova && $scope.isAndroid) setAndroidStatusBarColor();
if (scrollWatcherInitialized || !$scope.amountIsCollapsible) {
return;
}
scrollWatcherInitialized = true;
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.walletId = data.stateParams.walletId;
$scope.wallet = profileService.getWallet($scope.walletId);
$scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;
addressbookService.list(function(err, ab) {
@ -188,6 +348,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
});
$scope.updateAll();
refreshAmountSection();
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId) {
@ -201,9 +362,55 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
];
});
$scope.$on("$ionicView.beforeLeave", function(event, data) {
if ($window.StatusBar) {
$window.StatusBar.backgroundColorByHexString('#1e3186');
}
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
function setAndroidStatusBarColor() {
var SUBTRACT_AMOUNT = 15;
var rgb = hexToRgb($scope.wallet.color);
var keys = Object.keys(rgb);
keys.forEach(function(k) {
if (rgb[k] - SUBTRACT_AMOUNT < 0) {
rgb[k] = 0;
} else {
rgb[k] -= SUBTRACT_AMOUNT;
}
});
var statusBarColorHexString = rgbToHex(rgb.r, rgb.g, rgb.b);
if ($window.StatusBar)
$window.StatusBar.backgroundColorByHexString(statusBarColorHexString);
}
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
});

View file

@ -8,10 +8,11 @@ angular.module('copayApp.directives')
transclude: true,
scope: {
sendStatus: '=clickSendStatus',
wallet: '=hasWalletChosen'
},
link: function(scope, element, attrs) {
scope.$watch('sendStatus', function() {
if(scope.sendStatus !== 'success') {
if (scope.sendStatus !== 'success') {
scope.displaySendStatus = scope.sendStatus;
}
});

View file

@ -16,6 +16,6 @@ angular.module('copayApp.directives')
scope.emailHash = md5.createHash(scope.email.toLowerCase() || '');
}
},
template: '<img class="gravatar" alt="{{ name }}" height="{{ height }}" width="{{ width }}" src="https://secure.gravatar.com/avatar/{{ emailHash }}.jpg?s={{ width }}&d=mm">'
}
template: '<img class="gravatar" alt="{{ name }}" height="{{ height }}" width="{{ width }}" src="https://secure.gravatar.com/avatar/{{ emailHash }}.jpg?s={{ width }}&d=identicon">'
};
});

View file

@ -13,8 +13,8 @@ angular.module('copayApp.directives')
scope.showMenu = true;
scope.https = false;
if(scope.type === 'url') {
if(scope.data.indexOf('https://') === 0) {
if (scope.type === 'url') {
if (scope.data.indexOf('https://') === 0) {
scope.https = true;
}
}
@ -24,14 +24,16 @@ angular.module('copayApp.directives')
scope.showMenu = false;
$rootScope.$broadcast('incomingDataMenu.menuHidden');
};
scope.goToUrl = function(url){
scope.goToUrl = function(url) {
externalLinkService.open(url);
};
scope.sendPaymentToAddress = function(bitcoinAddress) {
scope.showMenu = false;
$state.go('tabs.send').then(function() {
$timeout(function() {
$state.transitionTo('tabs.send.amount', {toAddress: bitcoinAddress});
$state.transitionTo('tabs.send.amount', {
toAddress: bitcoinAddress
});
}, 50);
});
};
@ -40,11 +42,23 @@ angular.module('copayApp.directives')
$timeout(function() {
$state.go('tabs.send').then(function() {
$timeout(function() {
$state.transitionTo('tabs.send.addressbook', {addressbookEntry: bitcoinAddress});
$state.transitionTo('tabs.send.addressbook', {
addressbookEntry: bitcoinAddress
});
});
});
}, 100);
};
scope.scanPaperWallet = function(privateKey) {
scope.showMenu = false;
$state.go('tabs.home').then(function() {
$timeout(function() {
$state.transitionTo('tabs.home.paperWallet', {
privateKey: privateKey
});
}, 50);
});
};
}
};
});

View file

@ -8,7 +8,8 @@ angular.module('copayApp.directives')
transclude: true,
scope: {
sendStatus: '=slideSendStatus',
onConfirm: '&slideOnConfirm'
onConfirm: '&slideOnConfirm',
wallet: '=hasWalletChosen'
},
link: function(scope, element, attrs) {
@ -33,9 +34,9 @@ angular.module('copayApp.directives')
scope.displaySendStatus = '';
scope.$watch('sendStatus', function() {
if(!scope.sendStatus) {
if (!scope.sendStatus) {
reset();
} else if(scope.sendStatus === 'success') {
} else if (scope.sendStatus === 'success') {
scope.displaySendStatus = '';
$timeout(function() {
reset();
@ -51,19 +52,20 @@ angular.module('copayApp.directives')
var startTime = currentEaseStartTime;
var initialPct = fromPct;
var distance = pct - fromPct;
function ease() {
if(startTime !== currentEaseStartTime) {
if (startTime !== currentEaseStartTime) {
return;
}
$window.requestAnimationFrame(function() {
var now = Date.now();
var elapsed = now - startTime;
var normalizedElapsedTime = elapsed/duration;
var normalizedElapsedTime = elapsed / duration;
var newVal = easeFx(normalizedElapsedTime);
var newPct = newVal*distance + initialPct;
var newPct = newVal * distance + initialPct;
animateFx(newPct);
scope.$digest();
if(elapsed < duration) {
if (elapsed < duration) {
ease();
} else {
deferred.resolve();
@ -93,31 +95,33 @@ angular.module('copayApp.directives')
function setNewSliderStyle(pct) {
var knobWidthPct = getKnobWidthPercentage();
var translatePct = pct - knobWidthPct;
if(isSliding) {
translatePct += 0.35*pct;
if (isSliding) {
translatePct += 0.35 * pct;
}
scope.sliderStyle = getTransformStyle(translatePct);
curSliderPct = pct;
}
function setNewBitcoinStyle(pct) {
var translatePct = -2.25*pct;
var translatePct = -2.25 * pct;
scope.bitcoinStyle = getTransformStyle(translatePct);
curBitcoinPct = pct;
}
function setNewTextStyle(pct) {
var translatePct = -0.1*pct;
var translatePct = -0.1 * pct;
scope.textStyle = getTransformStyle(translatePct);
curTextPct = pct;
}
function getTransformStyle(translatePct) {
return {'transform': 'translateX(' + translatePct + '%)'};
return {
'transform': 'translateX(' + translatePct + '%)'
};
}
function getKnobWidthPercentage() {
var knobWidthPct = (KNOB_WIDTH/elm.clientWidth)*100;
function getKnobWidthPercentage() {
var knobWidthPct = (KNOB_WIDTH / elm.clientWidth) * 100;
return knobWidthPct;
}
@ -175,8 +179,8 @@ angular.module('copayApp.directives')
function getTouchXPosition($event) {
var x;
if($event.touches || $event.changedTouches) {
if($event.touches.length) {
if ($event.touches || $event.changedTouches) {
if ($event.touches.length) {
x = $event.touches[0].clientX;
} else {
x = $event.changedTouches[0].clientX;
@ -190,18 +194,18 @@ angular.module('copayApp.directives')
function getSlidPercentage($event) {
var x = getTouchXPosition($event);
var width = elm.clientWidth;
var pct = (x/width)*100;
if(x >= width) {
var pct = (x / width) * 100;
if (x >= width) {
pct = 100;
}
return pct;
}
scope.onTouchstart = function($event) {
if(scope.isSlidFully) {
if (scope.isSlidFully) {
return;
}
if(!isSliding) {
if (!isSliding) {
var pct = getSlidPercentage($event);
if (pct > MAX_SLIDE_START_PERCENTAGE) {
jiggleSlider();
@ -209,7 +213,7 @@ angular.module('copayApp.directives')
} else {
isSliding = true;
var knobWidthPct = getKnobWidthPercentage();
if(pct < knobWidthPct) {
if (pct < knobWidthPct) {
pct = knobWidthPct;
}
pct += PERCENTAGE_BUMP;
@ -219,12 +223,12 @@ angular.module('copayApp.directives')
};
scope.onTouchmove = function($event) {
if(!isSliding || scope.isSlidFully) {
if (!isSliding || scope.isSlidFully) {
return;
}
var pct = getSlidPercentage($event);
var knobWidthPct = getKnobWidthPercentage();
if(pct < knobWidthPct) {
if (pct < knobWidthPct) {
pct = knobWidthPct;
}
pct += PERCENTAGE_BUMP;
@ -233,11 +237,11 @@ angular.module('copayApp.directives')
};
scope.onTouchend = function($event) {
if(scope.isSlidFully) {
if (scope.isSlidFully) {
return;
}
var pct = getSlidPercentage($event);
if(isSliding && pct > FULLY_SLID_PERCENTAGE) {
if (isSliding && pct > FULLY_SLID_PERCENTAGE) {
pct = 100;
setSliderPosition(pct);
alertSlidFully();

View file

@ -7,6 +7,7 @@ angular.module('copayApp.directives')
templateUrl: 'views/includes/walletSelector.html',
transclude: true,
scope: {
title: '=walletSelectorTitle',
show: '=walletSelectorShow',
wallets: '=walletSelectorWallets',
selectedWallet: '=walletSelectorSelectedWallet',

View file

@ -151,7 +151,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
*/
.state('tabs.wallet', {
url: '/wallet/{walletId}/{fromOnboarding}',
url: '/wallet/:walletId/:fromOnboarding',
views: {
'tab-home@tabs': {
controller: 'walletDetailsController',
@ -182,7 +182,24 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
views: {
'tab-home@tabs': {
controller: 'txDetailsController',
templateUrl: 'views/modals/tx-details.html'
templateUrl: 'views/tx-details.html'
}
}
})
.state('tabs.wallet.backupWarning', {
url: '/backupWarning/:from/:walletId',
views: {
'tab-home@tabs': {
templateUrl: 'views/backupWarning.html'
}
}
})
.state('tabs.wallet.backup', {
url: '/backup/:walletId',
views: {
'tab-home@tabs': {
templateUrl: 'views/backup.html',
controller: 'backupController'
}
}
})
@ -269,7 +286,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('tabs.send.confirm', {
url: '/confirm/:isWallet/:toAddress/:toName/:toAmount/:toEmail/:description',
url: '/confirm/:isWallet/:toAddress/:toName/:toAmount/:toEmail/:description/:useSendMax',
views: {
'tab-send@tabs': {
controller: 'confirmController',
@ -538,15 +555,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
}
})
.state('tabs.preferences.paperWallet', {
url: '/paperWallet',
views: {
'tab-settings@tabs': {
controller: 'paperWalletController',
templateUrl: 'views/paperWallet.html'
}
}
})
/*
*
@ -601,7 +609,57 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
/*
*
* Back flow from receive
* Addresses
*
*/
.state('tabs.receive.addresses', {
url: '/addresses/:walletId',
views: {
'tab-receive@tabs': {
controller: 'addressesController',
templateUrl: 'views/addresses.html'
}
}
})
.state('tabs.receive.allAddresses', {
url: '/allAddresses/:walletId',
views: {
'tab-receive@tabs': {
controller: 'addressesController',
templateUrl: 'views/allAddresses.html'
}
}
})
/*
*
* Request Specific amount
*
*/
.state('tabs.receive.amount', {
url: '/amount/:customAmount/:toAddress',
views: {
'tab-receive@tabs': {
controller: 'amountController',
templateUrl: 'views/amount.html'
}
}
})
.state('tabs.receive.customAmount', {
url: '/customAmount/:toAmount/:toAddress',
views: {
'tab-receive@tabs': {
controller: 'customAmountController',
templateUrl: 'views/customAmount.html'
}
}
})
/*
*
* Init backup flow
*
*/
@ -625,10 +683,25 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
/*
*
* Onboarding
* Paper Wallet
*
*/
.state('tabs.home.paperWallet', {
url: '/paperWallet/:privateKey',
views: {
'tab-home@tabs': {
controller: 'paperWalletController',
templateUrl: 'views/paperWallet.html'
}
}
})
/*
*
* Onboarding
*
*/
.state('onboarding', {
url: '/onboarding',
abstract: true,
@ -695,7 +768,8 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
url: '/disclaimer/:walletId/:backedUp/:resume',
views: {
'onboarding': {
templateUrl: 'views/onboarding/disclaimer.html'
templateUrl: 'views/onboarding/disclaimer.html',
controller: 'disclaimerController'
}
}
})
@ -720,6 +794,67 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
},
})
/*
*
* Feedback
*
*/
.state('tabs.feedback', {
url: '/feedback',
views: {
'tab-settings@tabs': {
templateUrl: 'views/feedback/send.html',
controller: 'sendController'
}
}
})
.state('tabs.shareApp', {
url: '/shareApp/:score/:skipped/:fromSettings',
views: {
'tab-settings@tabs': {
controller: 'completeController',
templateUrl: 'views/feedback/complete.html'
}
}
})
.state('tabs.rate', {
url: '/rate',
abstract: true
})
.state('tabs.rate.send', {
url: '/send/:score',
views: {
'tab-home@tabs': {
templateUrl: 'views/feedback/send.html',
controller: 'sendController'
}
}
})
.state('tabs.rate.complete', {
url: '/complete/:score/:skipped',
views: {
'tab-home@tabs': {
controller: 'completeController',
templateUrl: 'views/feedback/complete.html'
}
},
customConfig: {
hideStatusBar: true
}
})
.state('tabs.rate.rateApp', {
url: '/rateApp/:score',
views: {
'tab-home@tabs': {
controller: 'rateAppController',
templateUrl: 'views/feedback/rateApp.html'
}
},
customConfig: {
hideStatusBar: true
}
})
/*
*
@ -753,30 +888,28 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
}
})
.state('tabs.buyandsell.glidera.buy', {
url: '/buy',
.state('tabs.buyandsell.glidera.amount', {
url: '/amount/:isGlidera/:glideraAccessToken',
views: {
'tab-home@tabs': {
controller: 'buyGlideraController',
controllerAs: 'buy',
templateUrl: 'views/buyGlidera.html'
controller: 'amountController',
templateUrl: 'views/amount.html'
}
}
})
.state('tabs.buyandsell.glidera.sell', {
url: '/sell',
.state('tabs.buyandsell.glidera.confirm', {
url: '/confirm/:toAmount/:isGlidera/:glideraAccessToken',
views: {
'tab-home@tabs': {
controller: 'sellGlideraController',
controllerAs: 'sell',
templateUrl: 'views/sellGlidera.html'
controller: 'confirmController',
templateUrl: 'views/confirm.html'
}
}
})
.state('tabs.buyandsell.glidera.preferences', {
url: '/preferences',
.state('tabs.preferences.glidera', {
url: '/glidera',
views: {
'tab-home@tabs': {
'tab-settings@tabs': {
controller: 'preferencesGlideraController',
templateUrl: 'views/preferencesGlidera.html'
}
@ -830,16 +963,36 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
controller: 'amazonController',
templateUrl: 'views/amazon.html'
}
},
params: {
cardClaimCode: null
}
})
.state('tabs.giftcards.amazon.buy', {
url: '/buy',
.state('tabs.giftcards.amazon.amount', {
url: '/amount',
views: {
'tab-home@tabs': {
controller: 'buyAmazonController',
controllerAs: 'buy',
templateUrl: 'views/buyAmazon.html'
controller: 'amountController',
templateUrl: 'views/amount.html'
}
},
params: {
isGiftCard: true,
toName: 'Amazon.com Gift Card'
}
})
.state('tabs.giftcards.amazon.confirm', {
url: '/confirm/:toAmount/:toAddress/:description/:giftCardAmountUSD/:giftCardAccessKey/:giftCardInvoiceTime/:giftCardUUID',
views: {
'tab-home@tabs': {
controller: 'confirmController',
templateUrl: 'views/confirm.html'
}
},
params: {
isGiftCard: true,
toName: 'Amazon.com Gift Card',
paypro: null
}
})
@ -889,10 +1042,10 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
paypro: null
}
})
.state('tabs.bitpayCard.preferences', {
url: '/preferences',
.state('tabs.preferences.bitpayCard', {
url: '/bitpay-card',
views: {
'tab-home@tabs': {
'tab-settings@tabs': {
controller: 'preferencesBitpayCardController',
templateUrl: 'views/preferencesBitpayCard.html'
}
@ -1030,4 +1183,14 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
$log.debug(' toParams:' + JSON.stringify(toParams || {}));
$log.debug(' fromParams:' + JSON.stringify(fromParams || {}));
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState, fromParams) {
if ($window.StatusBar) {
if (toState.customConfig && toState.customConfig.hideStatusBar) {
$window.StatusBar.hide();
} else {
$window.StatusBar.show();
}
}
});
});

View file

@ -70,7 +70,7 @@ angular.module('copayApp.services').factory('amazonService', function($http, $lo
});
// Show pending task from the UI
storageService.setNextStep('AmazonGiftCards', true, function(err) {});
storageService.setNextStep('AmazonGiftCards', 'true', function(err) {});
};
root.getPendingGiftCards = function(cb) {

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('backupService', function backupServiceFactory($log, $timeout, $stateParams, profileService, sjcl) {
.factory('backupService', function backupServiceFactory($log, $timeout, $stateParams, profileService, sjcl, $window) {
var root = {};
@ -80,7 +80,7 @@ angular.module('copayApp.services')
var walletName = (wallet.alias || '') + (wallet.alias ? '-' : '') + wallet.credentials.walletName;
if (opts.noSign) walletName = walletName + '-noSign'
var filename = walletName + '-Copaybackup.aes.json';
var filename = walletName + '-' + $window.appConfig.nameCase + 'backup.aes.json';
_download(ew, filename, cb)
};
return root;

View file

@ -7,7 +7,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
var _setError = function(msg, e) {
$log.error(msg);
var error = e.data ? e.data.error : msg;
var error = (e && e.data && e.data.error) ? e.data.error : msg;
return error;
};
@ -104,7 +104,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
};
root.bitAuthPair = function(obj, cb) {
var deviceName = 'Unknow device';
var deviceName = 'Unknown device';
if (platformInfo.isNW) {
deviceName = require('os').platform();
} else if (platformInfo.isCordova) {
@ -143,7 +143,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Not card found'));
if (!card) return cb(_setError('Card not found'));
// Get invoices
$http(_post('/api/v2/' + card.token, json, appIdentity)).then(function(data) {
$log.info('BitPay Get Invoices: SUCCESS');
@ -180,7 +180,7 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
root.getBitpayDebitCards(function(err, data) {
if (err) return cb(err);
var card = lodash.find(data, {id : cardId});
if (!card) return cb(_setError('Not card found'));
if (!card) return cb(_setError('Card not found'));
$http(_post('/api/v2/' + card.token, json, appIdentity)).then(function(data) {
$log.info('BitPay TopUp: SUCCESS');
if(data.data.error) {
@ -258,13 +258,30 @@ angular.module('copayApp.services').factory('bitpayCardService', function($http,
root.remove = function(card, cb) {
storageService.removeBitpayDebitCard(BITPAY_CARD_NETWORK, card, function(err) {
if (err) {
$log.error('Error removing BitPay debit card: ' + err);
// Continue, try to remove/cleanup card history
}
storageService.removeBitpayDebitCardHistory(BITPAY_CARD_NETWORK, card, function(err) {
$log.info('BitPay Debit Card(s) Removed: SUCCESS');
if (err) {
$log.error('Error removing BitPay debit card transaction history: ' + err);
return cb(err);
}
$log.info('Successfully removed BitPay debit card');
return cb();
});
});
};
root.getRates = function(currency, cb) {
$http(_get('/rates/' + currency)).then(function(data) {
$log.info('BitPay Get Rates: SUCCESS');
return cb(data.data.error, data.data.data);
}, function(data) {
return cb(_setError('BitPay Error: Get Rates', data));
});
};
/*
* CONSTANTS
*/

View file

@ -15,6 +15,16 @@ angular.module('copayApp.services').factory('configService', function(storageSer
url: 'https://bws.bitpay.com/bws/api',
},
download: {
url: 'https://bitpay.com/wallet',
},
rateApp: {
ios: 'http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=1149581638&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=8',
android: 'https://play.google.com/store/apps/details?id=com.bitpay.wallet',
wp: ''
},
// wallet default config
wallet: {
requiredCopayers: 2,

View file

@ -21,14 +21,10 @@ angular.module('copayApp.services').service('externalLinkService', function(plat
_restoreHandleOpenURL(old);
} else {
if (optIn) {
var message = gettextCatalog.getString(message),
title = gettextCatalog.getString(title),
okText = gettextCatalog.getString(okText),
cancelText = gettextCatalog.getString(cancelText),
openBrowser = function(res) {
if (res) window.open(url, '_system');
_restoreHandleOpenURL(old);
};
var openBrowser = function(res) {
if (res) window.open(url, '_system');
_restoreHandleOpenURL(old);
};
popupService.showConfirm(title, message, okText, cancelText, openBrowser);
} else {
window.open(url, '_system');

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services').factory('feeService', function($log, $stateParams, bwcService, walletService, configService, gettext, lodash, txFormatService) {
angular.module('copayApp.services').factory('feeService', function($log, $stateParams, bwcService, walletService, configService, gettext, lodash, txFormatService, gettextCatalog) {
var root = {};
// Constant fee options to translate
@ -15,48 +15,48 @@ angular.module('copayApp.services').factory('feeService', function($log, $stateP
return configService.getSync().wallet.settings.feeLevel || 'normal';
};
root.getCurrentFeeValue = function(cb) {
console.log('[feeService.js.18:getCurrentFeeValue:] TODO TODO TODO'); //TODO
// TODO TODO TODO
var wallet = profileService.getWallet($stateParams.walletId);
root.getCurrentFeeValue = function(network, cb) {
network = network || 'livenet';
var feeLevel = root.getCurrentFeeLevel();
wallet.getFeeLevels(wallet.credentials.network, function(err, levels) {
if (err)
return cb({
message: 'Could not get dynamic fee'
});
root.getFeeLevels(function(err, levels) {
if (err) return cb(err);
var feeLevelValue = lodash.find(levels, {
var feeLevelValue = lodash.find(levels[network], {
level: feeLevel
});
if (!feeLevelValue || !feeLevelValue.feePerKB)
if (!feeLevelValue || !feeLevelValue.feePerKB) {
return cb({
message: 'Could not get dynamic fee for level: ' + feeLevel
message: gettextCatalog.getString("Could not get dynamic fee for level: {{feeLevel}}", {
feeLevel: feeLevel
})
});
}
var fee = feeLevelValue.feePerKB;
$log.debug('Dynamic fee: ' + feeLevel + ' ' + fee + ' SAT');
return cb(null, fee);
});
};
root.getFeeLevels = function(cb) {
var walletClient = bwcService.getClient();
var unitName = configService.getSync().wallet.settings.unitName;
walletClient.getFeeLevels('livenet', function(errLivenet, levelsLivenet) {
walletClient.getFeeLevels('testnet', function(errTestnet, levelsTestnet) {
if (errLivenet || errTestnet) $log.debug('Could not get dynamic fee');
else {
if (errLivenet || errTestnet) {
return cb(gettextCatalog.getString('Could not get dynamic fee'));
} else {
for (var i = 0; i < 4; i++) {
levelsLivenet[i]['feePerKBUnit'] = txFormatService.formatAmount(levelsLivenet[i].feePerKB) + ' ' + unitName;
levelsTestnet[i]['feePerKBUnit'] = txFormatService.formatAmount(levelsTestnet[i].feePerKB) + ' ' + unitName;
}
}
return cb({
return cb(null, {
'livenet': levelsLivenet,
'testnet': levelsTestnet
});

View file

@ -0,0 +1,58 @@
'use strict';
angular.module('copayApp.services').factory('feedbackService', function($http, $log, $httpParamSerializer, configService) {
var root = {};
var URL = "https://script.google.com/macros/s/AKfycbybtvNSQKUfgzgXcj3jYLlvCKrcBoktjiJ1V8_cwd2yVkpUBGe3/exec";
root.send = function(dataSrc, cb) {
$http(_post(dataSrc)).then(function() {
$log.info("SUCCESS: Feedback sent");
return cb();
}, function(err) {
$log.info("ERROR: Feedback sent anyway.");
return cb(err);
});
};
var _post = function(dataSrc) {
return {
method: 'POST',
url: URL,
headers: {
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
data: $httpParamSerializer(dataSrc)
};
};
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

@ -81,7 +81,7 @@ angular.module('copayApp.services').factory('glideraService', function($http, $l
$http(req).then(function(data) {
$log.info('Glidera Authorization Access Token: SUCCESS');
// Show pending task from the UI
storageService.setNextStep('BuyAndSell', true, function(err) {});
storageService.setNextStep('BuyAndSell', 'true', function(err) {});
return cb(null, data.data);
}, function(data) {
$log.error('Glidera Authorization Access Token: ERROR ' + data.statusText);
@ -192,8 +192,13 @@ angular.module('copayApp.services').factory('glideraService', function($http, $l
};
root.get2faCode = function(token, cb) {
if (!token) return cb('Invalid Token');
if (!token) {
$log.error('Glidera Sent 2FA code by SMS: ERROR Invalid Token');
return cb('Invalid Token');
}
$http(_get('/authentication/get2faCode', token)).then(function(data) {
$log.info('Glidera Sent 2FA code by SMS: SUCCESS');
return cb(null, data.status == 200 ? true : false);
}, function(data) {

View file

@ -31,17 +31,31 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
if (!url) return;
name = name.replace(/[\[\]]/g, "\\$&");
var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
results = regex.exec(url);
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, " "));
}
function checkPrivateKey(privateKey) {
try {
new bitcore.PrivateKey(privateKey, 'livenet');
} catch (err) {
return false;
}
return true;
}
// data extensions for Payment Protocol with non-backwards-compatible request
if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
$state.go('tabs.send', {}, {'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true}).then(function() {
$state.transitionTo('tabs.send.confirm', {paypro: data});
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
}).then(function() {
$state.transitionTo('tabs.send.confirm', {
paypro: data
});
});
return true;
}
@ -55,31 +69,43 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
var addr = parsed.address ? parsed.address.toString() : '';
var message = parsed.message;
var amount = parsed.amount ? parsed.amount : '';
var amount = parsed.amount ? parsed.amount : '';
if (parsed.r) {
payproService.getPayProDetails(parsed.r, function(err, details) {
handlePayPro(details);
});
} else {
$state.go('tabs.send', {}, {'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true});
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
});
// Timeout is required to enable the "Back" button
$timeout(function() {
if (amount) {
$state.transitionTo('tabs.send.confirm', {toAmount: amount, toAddress: addr, description:message});
$state.transitionTo('tabs.send.confirm', {
toAmount: amount,
toAddress: addr,
description: message
});
} else {
$state.transitionTo('tabs.send.amount', {toAddress: addr});
$state.transitionTo('tabs.send.amount', {
toAddress: addr
});
}
}, 100);
}
return true;
// Plain URL
// Plain URL
} else if (/^https?:\/\//.test(data)) {
payproService.getPayProDetails(data, function(err, details) {
if(err) {
root.showMenu({data: data, type: 'url'});
if (err) {
root.showMenu({
data: data,
type: 'url'
});
return;
}
handlePayPro(details);
@ -87,47 +113,75 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
});
// Plain Address
} else if (bitcore.Address.isValid(data, 'livenet') || bitcore.Address.isValid(data, 'testnet')) {
if($state.includes('tabs.scan')) {
root.showMenu({data: data, type: 'bitcoinAddress'});
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'bitcoinAddress'
});
} else {
goToAmountPage(data);
}
} else if (data && data.indexOf($window.appConfig.name + '://glidera') === 0) {
return $state.go('uriglidera', {url: data});
return $state.go('uriglidera', {
url: data
});
} else if (data && data.indexOf($window.appConfig.name + '://coinbase') === 0) {
return $state.go('uricoinbase', {url: data});
return $state.go('uricoinbase', {
url: data
});
// BitPayCard Authentication
} else if (data && data.indexOf($window.appConfig.name + '://') === 0) {
var secret = getParameterByName('secret', data);
var email = getParameterByName('email', data);
var otp = getParameterByName('otp', data);
$state.go('tabs.home', {}, {'reload': true, 'notify': $state.current.name == 'tabs.home' ? false : true}).then(function() {
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
var secret = getParameterByName('secret', data);
var email = getParameterByName('email', data);
var otp = getParameterByName('otp', data);
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.bitpayCardIntro', {
secret: secret,
email: email,
otp: otp
});
return true;
});
return true;
// Join
// Join
} else if (data && data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home', {}, {'reload': true, 'notify': $state.current.name == 'tabs.home' ? false : true}).then(function() {
$state.transitionTo('tabs.add.join', {url: data});
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.add.join', {
url: data
});
});
return true;
// Old join
// Old join
} else if (data && data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
$state.go('tabs.home', {}, {'reload': true, 'notify': $state.current.name == 'tabs.home' ? false : true}).then(function() {
$state.transitionTo('tabs.add.join', {url: data});
$state.go('tabs.home', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.home' ? false : true
}).then(function() {
$state.transitionTo('tabs.add.join', {
url: data
});
});
return true;
} else if (data && (data.substring(0, 2) == '6P' || checkPrivateKey(data))) {
root.showMenu({
data: data,
type: 'privateKey'
});
} else {
if($state.includes('tabs.scan')) {
root.showMenu({data: data, type: 'text'});
if ($state.includes('tabs.scan')) {
root.showMenu({
data: data,
type: 'text'
});
}
}
@ -136,13 +190,18 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
};
function goToAmountPage(toAddress) {
$state.go('tabs.send', {}, {'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true});
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
});
$timeout(function() {
$state.transitionTo('tabs.send.amount', {toAddress: toAddress});
$state.transitionTo('tabs.send.amount', {
toAddress: toAddress
});
}, 100);
}
function handlePayPro(payProDetails){
function handlePayPro(payProDetails) {
var stateParams = {
toAmount: payProDetails.amount,
toAddress: payProDetails.toAddress,
@ -150,7 +209,10 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
paypro: payProDetails
};
scannerService.pausePreview();
$state.go('tabs.send', {}, {'reload': true, 'notify': $state.current.name == 'tabs.send' ? false : true}).then(function() {
$state.go('tabs.send', {}, {
'reload': true,
'notify': $state.current.name == 'tabs.send' ? false : true
}).then(function() {
$timeout(function() {
$state.transitionTo('tabs.send.confirm', stateParams);
});

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.services')
.factory('localStorageService', function(platformInfo, $timeout, $log) {
.factory('localStorageService', function(platformInfo, $timeout, $log, lodash) {
var isNW = platformInfo.isNW;
var isChromeApp = platformInfo.isChromeApp;
var root = {};
@ -45,6 +45,14 @@ angular.module('copayApp.services')
root.set = function(k, v, cb) {
if (isChromeApp || isNW) {
var obj = {};
if (lodash.isObject(v)) {
v = JSON.stringify(v);
}
if (!lodash.isString(v)) {
v = v.toString();
}
obj[k] = v;
chrome.storage.local.set(obj, cb);

View file

@ -25,7 +25,7 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
'recreating': gettext('Recreating Wallet...'),
'rejectTx': gettext('Rejecting payment proposal'),
'removeTx': gettext('Deleting payment proposal'),
'retrivingInputs': gettext('Retrieving inputs information'),
'retrievingInputs': gettext('Retrieving inputs information'),
'scanning': gettext('Scanning Wallet funds...'),
'sendingTx': gettext('Sending transaction'),
'signingTx': gettext('Signing transaction'),
@ -33,6 +33,13 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
'validatingWallet': gettext('Validating wallet integrity...'),
'validatingWords': gettext('Validating recovery phrase...'),
'loadingTxInfo': gettext('Loading transaction info...'),
'sendingFeedback': gettext('Sending feedback...'),
'generatingNewAddress': gettext('Generating new address...'),
'gettingAddresses': gettext('Getting addresses...'),
'sendingByEmail': gettext('Preparing addresses...'),
'sending2faCode': gettext('Sending 2FA code...'),
'buyingBitcoin': gettext('Buying Bitcoin...'),
'sellingBitcoin': gettext('Selling Bitcoin...')
};
root.clear = function() {
@ -64,7 +71,7 @@ angular.module('copayApp.services').factory('ongoingProcess', function($log, $ti
var showName = $filter('translate')(processNames[name] || name);
if(customHandler) {
if (customHandler) {
customHandler(processName, showName, isOn);
} else if (root.onGoingProcessName) {
if (isCordova) {

View file

@ -34,7 +34,8 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni
$ionicPopup.prompt({
title: title,
subTitle: message,
inputType: opts.inputType,
cssClass: opts.class,
template: '<input ng-model="data.response" type="' + opts.inputType + '" autofocus>',
inputPlaceholder: opts.inputPlaceholder,
defaultText: opts.defaultText
}).then(function(res) {
@ -51,12 +52,12 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni
var _cordovaConfirm = function(title, message, okText, cancelText, cb) {
var onConfirm = function(buttonIndex) {
if (buttonIndex == 1) return cb(true);
if (buttonIndex == 2) return cb(true);
else return cb(false);
}
okText = okText || gettextCatalog.getString('OK');
cancelText = cancelText || gettextCatalog.getString('Cancel');
navigator.notification.confirm(message, onConfirm, title, [okText, cancelText]);
navigator.notification.confirm(message, onConfirm, title, [cancelText, okText]);
};
var _cordovaPrompt = function(title, message, opts, cb) {
@ -118,7 +119,7 @@ angular.module('copayApp.services').service('popupService', function($log, $ioni
this.showPrompt = function(title, message, opts, cb) {
$log.warn(title ? (title + ': ' + message) : message);
opts = opts || {};
opts = opts ||  {};
if (isCordova && !opts.forceHTMLPrompt)
_cordovaPrompt(title, message, opts, cb);

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.services')
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, pushNotificationsService, gettext, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) {
.factory('profileService', function profileServiceFactory($rootScope, $timeout, $filter, $log, sjcl, lodash, storageService, bwcService, configService, pushNotificationsService, gettextCatalog, bwcError, uxLanguage, platformInfo, txFormatService, $state) {
var isChromeApp = platformInfo.isChromeApp;
@ -361,14 +361,14 @@ angular.module('copayApp.services')
} catch (ex) {
$log.info(ex);
return cb(gettext('Could not create: Invalid wallet recovery phrase'));
return cb(gettextCatalog.getString('Could not create: Invalid wallet recovery phrase'));
}
} else if (opts.extendedPrivateKey) {
try {
walletClient.seedFromExtendedPrivateKey(opts.extendedPrivateKey);
} catch (ex) {
$log.warn(ex);
return cb(gettext('Could not create using the specified extended private key'));
return cb(gettextCatalog.getString('Could not create using the specified extended private key'));
}
} else if (opts.extendedPublicKey) {
try {
@ -378,7 +378,7 @@ angular.module('copayApp.services')
});
} catch (ex) {
$log.warn("Creating wallet from Extended Public Key Arg:", ex, opts);
return cb(gettext('Could not create using the specified extended public key'));
return cb(gettextCatalog.getString('Could not create using the specified extended public key'));
}
} else {
var lang = uxLanguage.getCurrentLanguage();
@ -421,7 +421,7 @@ angular.module('copayApp.services')
singleAddress: opts.singleAddress,
walletPrivKey: opts.walletPrivKey,
}, function(err, secret) {
if (err) return bwcError.cb(err, gettext('Error creating wallet'), cb);
if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb);
return cb(null, walletClient, secret);
});
});
@ -451,11 +451,11 @@ angular.module('copayApp.services')
if (lodash.find(root.profile.credentials, {
'walletId': walletData.walletId
})) {
return cb(gettext('Cannot join the same wallet more that once'));
return cb(gettextCatalog.getString('Cannot join the same wallet more that once'));
}
} catch (ex) {
$log.debug(ex);
return cb(gettext('Bad wallet invitation'));
return cb(gettextCatalog.getString('Bad wallet invitation'));
}
opts.networkName = walletData.network;
$log.debug('Joining Wallet:', opts);
@ -464,7 +464,7 @@ angular.module('copayApp.services')
if (err) return cb(err);
walletClient.joinWallet(opts.secret, opts.myName || 'me', {}, function(err) {
if (err) return bwcError.cb(err, gettext('Could not join wallet'), cb);
if (err) return bwcError.cb(err, gettextCatalog.getString('Could not join wallet'), cb);
addAndBindWalletClient(walletClient, {
bwsurl: opts.bwsurl
}, cb);
@ -521,12 +521,12 @@ angular.module('copayApp.services')
// Adds and bind a new client to the profile
var addAndBindWalletClient = function(client, opts, cb) {
if (!client || !client.credentials)
return cb(gettext('Could not access wallet'));
return cb(gettextCatalog.getString('Could not access wallet'));
var walletId = client.credentials.walletId
if (!root.profile.addWallet(JSON.parse(client.export())))
return cb(gettext('Wallet already in Copay'));
return cb(gettextCatalog.getString('Wallet already in Copay'));
var skipKeyValidation = root.profile.isChecked(platformInfo.ua, walletId);
@ -595,15 +595,7 @@ angular.module('copayApp.services')
password: opts.password
});
} catch (err) {
return cb(gettext('Could not import. Check input file and spending password'));
}
if (walletClient.hasPrivKeyEncrypted()) {
try {
walletClient.disablePrivateKeyEncryption();
} catch (e) {
$log.warn(e);
}
return cb(gettextCatalog.getString('Could not import. Check input file and spending password'));
}
str = JSON.parse(str);
@ -634,7 +626,7 @@ angular.module('copayApp.services')
if (err instanceof errors.NOT_AUTHORIZED)
return cb(err);
return bwcError.cb(err, gettext('Could not import'), cb);
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
}
addAndBindWalletClient(walletClient, {
@ -665,7 +657,7 @@ angular.module('copayApp.services')
if (err instanceof errors.NOT_AUTHORIZED)
return cb(err);
return bwcError.cb(err, gettext('Could not import'), cb);
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
}
addAndBindWalletClient(walletClient, {
@ -688,7 +680,7 @@ angular.module('copayApp.services')
if (err instanceof errors.NOT_AUTHORIZED)
err.name = 'WALLET_DOES_NOT_EXIST';
return bwcError.cb(err, gettext('Could not import'), cb);
return bwcError.cb(err, gettextCatalog.getString('Could not import'), cb);
}
addAndBindWalletClient(walletClient, {
@ -773,6 +765,12 @@ angular.module('copayApp.services')
});
}
if (opts.m) {
ret = lodash.filter(ret, function(w) {
return (w.credentials.m == opts.m);
});
}
if (opts.onlyComplete) {
ret = lodash.filter(ret, function(w) {
return w.isComplete();
@ -795,7 +793,7 @@ angular.module('copayApp.services')
root.getNotifications = function(opts, cb) {
opts = opts || {};
var TIME_STAMP = 60 * 60 * 24 * 7;
var TIME_STAMP = 60 * 60 * 6;
var MAX = 100;
var typeFilter = {
@ -861,26 +859,25 @@ angular.module('copayApp.services')
var finale = shown; // GROUPING DISABLED!
// var finale = [],
// prev;
//
//
// // Item grouping... DISABLED.
//
// // REMOVE (if we want 1-to-1 notification) ????
// lodash.each(shown, function(x) {
// if (prev && prev.walletId === x.walletId && prev.txpId && prev.txpId === x.txpId && prev.creatorId && prev.creatorId === x.creatorId) {
// prev.types.push(x.type);
// prev.data = lodash.assign(prev.data, x.data);
// prev.txid = prev.txid || x.txid;
// prev.amountStr = prev.amountStr || x.amountStr;
// prev.creatorName = prev.creatorName || x.creatorName;
// } else {
// finale.push(x);
// prev = x;
// }
// });
//
var finale = [],
prev;
// Item grouping... DISABLED.
// REMOVE (if we want 1-to-1 notification) ????
lodash.each(shown, function(x) {
if (prev && prev.walletId === x.walletId && prev.txpId && prev.txpId === x.txpId && prev.creatorId && prev.creatorId === x.creatorId) {
prev.types.push(x.type);
prev.data = lodash.assign(prev.data, x.data);
prev.txid = prev.txid || x.txid;
prev.amountStr = prev.amountStr || x.amountStr;
prev.creatorName = prev.creatorName || x.creatorName;
} else {
finale.push(x);
prev = x;
}
});
var u = bwcService.getUtils();
lodash.each(finale, function(x) {

View file

@ -111,18 +111,24 @@ RateService.prototype.fromFiat = function(amount, code) {
return amount / this.getRate(code) * this.BTC_TO_SAT;
};
RateService.prototype.listAlternatives = function() {
RateService.prototype.listAlternatives = function(sort) {
var self = this;
if (!this.isAvailable()) {
return [];
}
return self.lodash.map(this.getAlternatives(), function(item) {
var alternatives = self.lodash.map(this.getAlternatives(), function(item) {
return {
name: item.name,
isoCode: item.isoCode
}
});
if (sort) {
alternatives.sort(function(a, b) {
return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1;
});
}
return self.lodash.uniq(alternatives, 'isoCode');
};
angular.module('copayApp.services').factory('rateService', function($http, lodash) {

View file

@ -77,23 +77,23 @@ 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
// `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
// 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
'01_bitpayCardCredentials' : _upgrade_bitpayCardCredentials // 2016-11: Upgrade bitpayCardCredentials-x to appIdentity-x
@ -244,6 +244,14 @@ angular.module('copayApp.services')
storage.remove('profile', cb);
};
root.setFeedbackInfo = function(feedbackValues, cb) {
storage.set('feedback', feedbackValues, cb);
};
root.getFeedbackInfo = function(cb) {
storage.get('feedback', cb);
};
root.storeFocusedWalletId = function(id, cb) {
storage.set('focusedWalletId', id || '', cb);
};
@ -390,6 +398,14 @@ angular.module('copayApp.services')
storage.remove('nextStep-' + service, cb);
};
root.setLastCurrencyUsed = function(lastCurrencyUsed, cb) {
storage.set('lastCurrencyUsed', lastCurrencyUsed, cb)
};
root.getLastCurrencyUsed = function(cb) {
storage.get('lastCurrencyUsed', cb)
};
root.checkQuota = function() {
var block = '';
// 50MB
@ -459,7 +475,10 @@ angular.module('copayApp.services')
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) || {};
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
bitpayAccounts[data.email] = bitpayAccounts[data.email] || {};
bitpayAccounts[data.email]['bitpayDebitCards-' + network] = data;
storage.set('bitpayAccounts-' + network, JSON.stringify(bitpayAccounts), cb);
@ -468,7 +487,10 @@ angular.module('copayApp.services')
root.getBitpayDebitCards = function(network, cb) {
storage.get('bitpayAccounts-' + network, function(err, bitpayAccounts) {
bitpayAccounts = JSON.parse(bitpayAccounts) || {};
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
var cards = [];
Object.keys(bitpayAccounts).forEach(function(email) {
// For the UI, add the account email to the card object.
@ -490,15 +512,20 @@ angular.module('copayApp.services')
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) || {};
if (lodash.isString(bitpayAccounts)) {
bitpayAccounts = JSON.parse(bitpayAccounts);
}
bitpayAccounts = bitpayAccounts || {};
Object.keys(bitpayAccounts).forEach(function(userId) {
var data = bitpayAccounts[userId]['bitpayDebitCards-' + network];
var newCards = lodash.reject(data.cards, {'eid': card.eid});
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){
root.getBitpayDebitCards(network, function(err, cards) {
if (err) cb(err);
if (cards.length == 0) {
root.removeNextStep('BitpayCard', cb);

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.services')
.factory('trezor', function($log, $timeout, gettext, lodash, bitcore, hwWallet) {
.factory('trezor', function($log, $timeout, lodash, bitcore, hwWallet) {
var root = {};
var SETTLE_TIME = 3000;
@ -82,7 +82,7 @@ angular.module('copayApp.services')
if (txp.outputs.length > 1)
return callback('Only single output TXPs are supported in TREZOR');
} else {
return callback('Unknown TXP at TREZOR');
return callback('Unknown TXP at TREZOR');
}
if (txp.outputs) {

View file

@ -8,42 +8,39 @@ angular.module('copayApp.services')
root.availableLanguages = [{
name: 'English',
isoCode: 'en',
}, {
name: 'Český',
isoCode: 'cs',
}, {
name: 'Français',
isoCode: 'fr',
}, {
name: 'Italiano',
isoCode: 'it',
}, {
name: 'Deutsch',
isoCode: 'de',
}, {
name: 'Español',
isoCode: 'es',
}, {
name: '日本語',
isoCode: 'ja',
useIdeograms: true,
}, {
name: '中文(简体)',
isoCode: 'zh',
useIdeograms: true,
}, {
name: 'Polski',
isoCode: 'pl',
}, {
name: 'Pусский',
isoCode: 'ru',
name: 'Français',
isoCode: 'fr',
// }, {
// name: 'Český',
// isoCode: 'cs',
// }, {
// name: 'Italiano',
// isoCode: 'it',
// }, {
// name: 'Deutsch',
// isoCode: 'de',
// }, {
// name: '日本語',
// isoCode: 'ja',
// useIdeograms: true,
// }, {
// name: '中文(简体)',
// isoCode: 'zh',
// useIdeograms: true,
// }, {
// name: 'Polski',
// isoCode: 'pl',
// }, {
// name: 'Pусский',
// isoCode: 'ru',
}];
root._detect = function(cb) {
return cb('en'); //disable auto detection for release;
var userLang, androidLang;
if (navigator && navigator.globalization) {

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;
@ -180,8 +144,9 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (err instanceof errors.NOT_AUTHORIZED) {
return cb('WALLET_NOT_REGISTERED');
}
return cb(bwcError.msg(err, gettext('Could not update Wallet')));
return cb(err);
}
return cb(null, ret);
});
};
@ -196,19 +161,22 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
// Address with Balance
cache.balanceByAddress = balance.byAddress;
// Total wallet balance is same regardless of 'spend unconfirmed funds' setting.
cache.totalBalanceSat = balance.totalAmount;
// Spend unconfirmed funds
if (config.spendUnconfirmed) {
cache.totalBalanceSat = balance.totalAmount;
cache.lockedBalanceSat = balance.lockedAmount;
cache.availableBalanceSat = balance.availableAmount;
cache.totalBytesToSendMax = balance.totalBytesToSendMax;
cache.pendingAmount = null;
cache.pendingAmount = 0;
cache.spendableAmount = balance.totalAmount - balance.lockedAmount;
} else {
cache.totalBalanceSat = balance.totalConfirmedAmount;
cache.lockedBalanceSat = balance.lockedConfirmedAmount;
cache.availableBalanceSat = balance.availableConfirmedAmount;
cache.totalBytesToSendMax = balance.totalBytesToSendConfirmedMax;
cache.pendingAmount = balance.totalAmount - balance.totalConfirmedAmount;
cache.spendableAmount = balance.totalConfirmedAmount - balance.lockedAmount;
}
// Selected unit
@ -220,25 +188,35 @@ 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;
if (cache.pendingAmount !== null && cache.pendingAmount !== 0) {
cache.pendingAmountStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + cache.unitName;
} else {
cache.pendingAmountStr = null;
}
cache.spendableBalanceStr = txFormatService.formatAmount(cache.spendableAmount) + ' ' + cache.unitName;
cache.pendingBalanceStr = txFormatService.formatAmount(cache.pendingAmount) + ' ' + cache.unitName;
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);
var pendingBalanceAlternative = rateService.toFiat(cache.pendingAmount, cache.alternativeIsoCode);
var lockedBalanceAlternative = rateService.toFiat(cache.lockedBalanceSat, cache.alternativeIsoCode);
var spendableBalanceAlternative = rateService.toFiat(cache.spendableAmount, cache.alternativeIsoCode);
var alternativeConversionRate = rateService.toFiat(100000000, cache.alternativeIsoCode);
cache.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative);
cache.pendingBalanceAlternative = $filter('formatFiatAmount')(pendingBalanceAlternative);
cache.lockedBalanceAlternative = $filter('formatFiatAmount')(lockedBalanceAlternative);
cache.spendableBalanceAlternative = $filter('formatFiatAmount')(spendableBalanceAlternative);
cache.alternativeConversionRate = $filter('formatFiatAmount')(alternativeConversionRate);
cache.alternativeBalanceAvailable = true;
@ -337,7 +315,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return tx.txid != endingTxid;
});
return cb(null, res, res.length == limit);
return cb(null, res, res.length >= limit);
});
};
@ -428,7 +406,16 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
function getNewTxs(newTxs, skip, cb) {
getTxsFromServer(wallet, skip, endingTxid, requestLimit, function(err, res, shouldContinue) {
if (err) return cb(err);
if (err) {
$log.warn(bwcError.msg(err, 'BWS Error')); //TODO
if (err instanceof errors.CONNECTION_ERROR || (err.message && err.message.match(/5../))) {
log.info('Retrying history download in 5 secs...');
return $timeout(function() {
return getNewTxs(newTxs, skip, cb);
}, 5000);
};
return cb(err);
}
newTxs = newTxs.concat(processNewTxs(wallet, lodash.compact(res)));
@ -587,20 +574,13 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (lodash.isEmpty(txp) || lodash.isEmpty(wallet))
return cb('MISSING_PARAMETER');
if (txp.sendMax) {
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else return cb(null, createdTxp);
});
} else {
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else {
$log.debug('Transaction created');
return cb(null, createdTxp);
}
});
}
wallet.createTxProposal(txp, function(err, createdTxp) {
if (err) return cb(err);
else {
$log.debug('Transaction created');
return cb(null, createdTxp);
}
});
};
root.publishTx = function(wallet, txp, cb) {
@ -760,12 +740,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);
});
};
@ -797,13 +778,30 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
});
};
root.getAddress = function(wallet, forceNew, cb) {
root.getMainAddresses = function(wallet, opts, cb) {
opts = opts || {};
opts.reverse = true;
wallet.getMainAddresses(opts, function(err, addresses) {
return cb(err, addresses);
});
};
root.getBalance = function(wallet, opts, cb) {
opts = opts || {};
wallet.getBalance(opts, function(err, resp) {
return cb(err, resp);
});
};
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() {
@ -829,7 +827,8 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
var askPassword = function(name, title, cb) {
var opts = {
inputType: 'password',
forceHTMLPrompt: true
forceHTMLPrompt: true,
class: 'text-warn'
};
popupService.showPrompt(title, name, opts, function(res) {
if (!res) return cb();
@ -839,9 +838,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.encrypt = function(wallet, cb) {
askPassword(wallet.name, gettext('Enter new spending password'), function(password) {
var title = gettextCatalog.getString('Enter new spending password');
var warnMsg = gettextCatalog.getString('Your wallet key will be encrypted. The Spending Password cannot be recovered. Be sure to write it down.');
askPassword(warnMsg, title, function(password) {
if (!password) return cb('no password');
askPassword(wallet.name, gettext('Confirm you new spending password'), function(password2) {
title = gettextCatalog.getString('Confirm you new spending password');
askPassword(warnMsg, gettextCatalog.getString('Confirm you new spending password'), function(password2) {
if (!password2 || password != password2)
return cb('password mismatch');
@ -854,7 +856,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.decrypt = function(wallet, cb) {
$log.debug('Disabling private key encryption for' + wallet.name);
askPassword(wallet.name, gettext('Enter Spending Password'), function(password) {
askPassword(null, gettextCatalog.getString('Enter Spending Password'), function(password) {
if (!password) return cb('no password');
try {
@ -869,7 +871,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
root.handleEncryptedWallet = function(wallet, cb) {
if (!root.isEncrypted(wallet)) return cb();
askPassword(wallet.name, gettext('Enter Spending Password'), function(password) {
askPassword(wallet.name, gettextCatalog.getString('Enter Spending Password'), function(password) {
if (!password) return cb('No password');
if (!wallet.checkPassword(password)) return cb('Wrong password');
@ -892,20 +894,14 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
};
root.onlyPublish = function(wallet, txp, cb) {
ongoingProcess.set('sendingTx', true);
root.onlyPublish = function(wallet, txp, cb, customStatusHandler) {
ongoingProcess.set('sendingTx', true, customStatusHandler);
root.publishTx(wallet, txp, function(err, publishedTxp) {
root.invalidateCache(wallet);
ongoingProcess.set('sendingTx', false);
if (err) return cb(err);
var type = root.getViewStatus(wallet, createdTxp);
root.openStatusModal(type, createdTxp, function() {
$rootScope.$emit('Local/TxAction', wallet.id);
return;
});
return cb(null, publishedTxp);
ongoingProcess.set('sendingTx', false, customStatusHandler);
if (err) return cb(bwcError.msg(err));
$rootScope.$emit('Local/TxAction', wallet.id);
return cb();
});
};
@ -934,13 +930,13 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
}
root.prepare(wallet, function(err, password) {
if (err) return cb('Prepare error: ' + err);
if (err) return cb(bwcError.msg(err));
ongoingProcess.set('sendingTx', true, customStatusHandler);
publishFn(wallet, txp, function(err, publishedTxp) {
ongoingProcess.set('sendingTx', false, customStatusHandler);
if (err) return cb('Send Error: ' + err);
if (err) return cb(bwcError.msg(err));
ongoingProcess.set('signingTx', true, customStatusHandler);
root.signTx(wallet, publishedTxp, password, function(err, signedTxp) {
@ -950,10 +946,9 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
if (err) {
$log.warn('sign error:' + err);
// TODO?
var msg = err.message ?
var msg = err && err.message ?
err.message :
gettext('The payment was created but could not be completed. Please try again from home screen');
gettextCatalog.getString('The payment was created but could not be completed. Please try again from home screen');
$rootScope.$emit('Local/TxAction', wallet.id);
return cb(msg);
@ -963,7 +958,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
ongoingProcess.set('broadcastingTx', true, customStatusHandler);
root.broadcastTx(wallet, signedTxp, function(err, broadcastedTxp) {
ongoingProcess.set('broadcastingTx', false, customStatusHandler);
if (err) return cb('sign error' + err);
if (err) return cb(bwcError.msg(err));
$rootScope.$emit('Local/TxAction', wallet.id);
var type = root.getViewStatus(wallet, broadcastedTxp);
@ -1090,6 +1085,12 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return type;
};
root.getSendMaxInfo = function(wallet, opts, cb) {
opts = opts || {};
wallet.getSendMaxInfo(opts, function(err, res) {
return cb(err, res);
});
};
return root;
});

View file

@ -19,8 +19,7 @@ $button-secondary-border: transparent;
$button-secondary-active-bg: darken($subtle-gray, 5%);
$button-secondary-active-border: transparent;
%button-standard,
click-to-accept {
%button-standard {
width: 85%;
max-width: 300px;
margin-left: auto;

View file

@ -6,9 +6,6 @@
/* Ionic Overrides and Workarounds */
// Please include a description of the problem solved by the workaround.
// class to dynamically hide the ion-nav-bar for v1 Amazon flow
ion-nav-bar.hide { display: block !important; }
// the ion tabs element never needs it's own background (backgrounds are
// rendered by the tabs), and the default background would cover the scanner
ion-tabs.ion-tabs-transparent {
@ -40,3 +37,20 @@ $placeholder-icon-padding: 10px;
.card {
margin: ($content-padding * 2) 14px;
}
// A somewhat dirty solution to the nav-bar "flashing" during page transitions.
// Since the old nav-bar is hidden before the new one is shown, this pseudo
// element fills the space with the proper background color.
ion-view.deflash-blue:before {
content: " ";
display: block;
position: absolute;
top: 0;
left: 0;
right: 0;
height: $bar-height;
background-color: $royal;
}
.platform-ios.platform-cordova:not(.fullscreen) ion-view.deflash-blue:before {
height: $bar-height + $ios-statusbar-height;
}

View file

@ -30,6 +30,17 @@
}
}
.text-warn {
.popup-sub-title {
margin-top: 15px;
color: #e42112 !important;
}
}
.item-toggle, .item {
white-space: normal;
}
.input-label {
max-width: none;
width: inherit;
@ -437,3 +448,7 @@ input[type=file] {
.keyboard-open .has-tabs {
bottom: 0;
}
.white-space-initial {
white-space: initial;
}

View file

@ -20,6 +20,7 @@
}
}
#view-address-book {
@extend .deflash-blue;
.scroll {
height:100%;
}

View file

@ -0,0 +1,89 @@
#addresses {
.addr {
&-explanation, &-button-group {
padding: 0 1rem;
margin: 1rem 0;
}
&-description {
text-align: center;
font-size: 15px;
color: $mid-gray;
margin: 1rem 0;
a {
font-weight: bold;
cursor: pointer;
cursor: hand;
}
}
&-balance {
margin-top: 4px;
color: #5DD263;
}
&-path {
margin-top: 4px;
color: #B8B8B8;
}
}
.banner-icon {
margin-top: 25px;
i {
box-shadow: $hovering-box-shadow;
}
}
.addr-list {
.item {
color: $dark-gray;
padding-top: 1.3rem;
padding-bottom: 1.3rem;
&.has-addr-value {
padding-top: .65rem;
padding-bottom: .65rem;
}
&.item-divider {
color: $mid-gray;
padding-bottom: .5rem;
font-size: .9rem;
}
&.view-all {
margin: 20px 0px 20px 0px;
cursor: pointer;
cursor: hand;
i {
font-size: 35px;
margin-right: 5px;
color: #647ce8;
}
span {
color: #647ce8;
font-weight: bold;
}
}
i {
font-size: 35px;
margin-right: 2px;
}
}
.box-error {
padding: 25px;
background-color: #E65555;
color: #F4F4F4;
h5 {
margin: 5px;
color: #F4F4F4;
text-align: center;
font-weight: bold;
}
a {
font-weight: bold;
color: #F4F4F4;
cursor: pointer;
cursor: hand;
}
}
.item-note {
color: $light-gray;
}
}
}

View file

@ -1,4 +1,5 @@
#view-amount {
@extend .deflash-blue;
.recipient-label {
font-size: 14px;
padding-bottom: 0;
@ -10,6 +11,9 @@
.icon-bitpay-card {
background-image: url("../img/icon-bitpay.svg");
}
.icon-amazon {
background-image: url("../img/icon-amazon.svg");
}
@media(max-width: 480px) {
.bitcoin-address {
.icon {
@ -33,7 +37,7 @@
position: absolute;
top: 10px;
}
.amount-pane {
.amount-pane-recipient {
position: absolute;
top: 95px;
bottom: 0;
@ -64,6 +68,42 @@
}
}
}
.amount-pane-no-recipient {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
background-color: #fff;
padding: 0 16px;
.amount-bar {
padding: 24px 0;
font-size: 18px;
.title {
float: left;
padding-top: 10px;
color: $dark-gray;
font-weight: bold;
.limits {
margin-top: 10px;
color: $light-gray;
font-size: 12px;
}
}
}
.amount {
display: flex;
flex-direction: column;
justify-content: center;
flex-grow: 1;
position: absolute;
bottom: 254px;
top: 66px;
.light {
color: $light-gray;
}
}
}
.amount {
&__editable {
margin-bottom: 1rem;

View file

@ -1,4 +1,5 @@
#bitpayCard {
background: white;
.bar-header {
border: 0;
background: #1e3186;
@ -9,15 +10,35 @@
background-color: transparent;
}
}
.amount-wrapper {
position: relative;
overflow: visible;
.amount-bg {
content: '';
top: -1000px;
left: 0;
position: absolute;
height: 1000px;
width: 100%;
background-color: #1e3186;
}
}
.amount {
width: 100%;
text-align: center;
padding: 2rem 1rem 1.5rem 1rem;
height: 140px;
height: 160px;
border-color: #172565;
background-color: #1e3186;
background-image: linear-gradient(0deg, #172565, #172565 0%, transparent 0%);
color: #fff;
&__balance {
margin-bottom: 25px;
font-weight: 600;
font-size: 34px;
}
}
.wallet-details-wallet-info {
bottom: 5px;
@ -37,4 +58,26 @@
.item-select select {
color: #667;
}
.get-started {
margin-top: 20px;
&__arrow {
font-size: 56px;
opacity: .2;
}
h1 {
font-size: 28px;
color: #4A4A4A;
}
&__text {
font-weight: 300;
color: #8e8e8e;
max-width: 300px;
margin: 0 auto;
}
}
}

View file

@ -11,5 +11,12 @@
color: $light-gray;
font-size: 14px;
}
&.item-icon-right {
.icon-hotspot {
right: 0px;
padding-left: 11px;
padding-right: 11px;
}
}
}
}

View file

@ -1,3 +1,9 @@
#view-confirm {
@extend .deflash-blue;
.icon-amazon {
background-image: url("../img/icon-amazon.svg");
}
.tx-details-content > .scroll {
padding-bottom: .25rem;
}
}

View file

@ -0,0 +1,16 @@
#custom-amount {
.share {
justify-content: center;
}
.info {
.single-line {
display: flex;
align-items: center;
padding-top: 17px;
padding-bottom: 17px;
.item-note {
margin-left: 10px;
}
}
}
}

View file

@ -0,0 +1,94 @@
#complete {
background-color: #fff;
.close-button {
color: $dark-gray;
position: absolute;
top: 5px;
right: 10px;
padding: 15px;
font-size: 36px;
}
.complete-layout {
display: flex;
flex-direction: column;
height: 100%;
&__expand {
display: flex;
flex-direction: column;
flex-grow: 1;
align-items: center;
justify-content: center;
text-align: center;
opacity: 0;
transition: opacity .3s;
&.fade-in {
opacity: 1;
}
}
}
.share-the-love-illustration {
width: 5rem;
margin: 1rem;
}
.send-feedback-illustration {
height: 16rem;
margin: 1rem;
}
.feedback-title {
font-size: 20px;
font-weight: bold;
color: $dark-gray;
margin: 20px 10px;
text-align: center;
}
.subtitle {
padding: 10px 30px 20px;
text-align: center;
color: $mid-gray;
}
.icon-svg > img {
height: 16rem;
width: 16rem;
margin: 10px;
}
.socialsharing-icon {
display: inline-block;
width: 60px;
}
.addressbook-icon-svg {
display: inline-block;
width: 50px;
height: 50px;
}
.share-buttons {
padding: 50px 10px 30px;
background-color: $subtle-gray;
text-align: center;
transform: translateY(100%);
opacity: 0;
animation-name: slideUpFadeIn;
animation-duration: .3s;
animation-fill-mode: forwards;
animation-timing-function: cubic-bezier(0.4, 0.0, 0.2, 1);
animation-delay: .2s;
}
.share-buttons__action {
display: inline-block;
color: #667;
font-size: .9rem;
width: 90px;
height: 90px;
margin-bottom: 20px;
}
}
@keyframes slideUpFadeIn {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}

View file

@ -0,0 +1,38 @@
#rate-app {
background-color: #ffffff;
text-align: center;
.skip-rating {
color: $dark-gray;
position: absolute;
top: 5px;
right: 10px;
padding: 15px;
}
.icon-svg > img {
width: 80px;
height: 80px;
margin-top: 15px;
}
.feedback-title {
font-size: 20px;
font-weight: bold;
color: $dark-gray;
margin: 80px 50px 10px;
text-align: center;
}
.share-the-love-illustration {
width: 5rem;
margin: 1rem;
}
.subtitle {
padding: 10px 30px 20px 40px;
color: #667;
}
.rate-buttons {
bottom: 0;
width: 100%;
position: absolute;
background-color: $subtle-gray;
padding: 30px 0 15px;
}
}

View file

@ -0,0 +1,18 @@
#rate-card {
.item-heading {
font-weight: 700;
}
.row {
border: none;
}
.item-icon-right {
margin: 0;
}
.feedback-flow-button {
margin-bottom: 20px;
}
.icon-svg > img {
height: 1.8rem;
margin-bottom: 5px;
}
}

View file

@ -0,0 +1,54 @@
#send-feedback {
@extend .deflash-blue;
background-color: #ffffff;
.row {
border: none;
}
.skip {
color: rgba(255, 255, 255, 0.3);
}
.feedback-heading {
padding-top: 20px
}
.feedback-title {
padding-left: 10px;
font-size: 20px;
font-weight: bold;
color: $dark-gray;
}
.rating {
text-align: right;
padding-right: 15px;
}
.comment {
padding: 0 20px 20px;
font-size: 1rem;
line-height: 1.5em;
font-weight: 300;
color: $dark-gray;
}
.user-feedback {
border-top: 1px solid $subtle-gray;
border-bottom: 1px solid $subtle-gray;
padding: 20px;
width: 100%;
margin-bottom: 20px;
-webkit-appearance: none;
}
.send-feedback-star {
height: 1rem;
margin-left: 5px;
}
.form-fade-in {
opacity: 0;
animation-name: fadeIn;
animation-duration: .5s;
animation-fill-mode: forwards;
animation-timing-function: ease-in;
}
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

View file

@ -1,7 +1,18 @@
click-to-accept {
position: fixed;
bottom: 0;
height: 92px;
width: 100%;
.click-to-accept {
&__button.button.button-primary.button-standard {
height: 100%;
max-width: 9999px;
width: 100%;
}
&__button.disable {
pointer-events: none;
}
@ -18,6 +29,9 @@ click-to-accept {
transform: translateY(2rem);
opacity: 0;
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
&.enter {
transition: transform 250ms ease, opacity 250ms ease;

View file

@ -0,0 +1,12 @@
#menu-popover {
border-radius: 5px;
.list {
.item {
cursor: pointer;
cursor: hand;
&:hover {
background-color: #E4E2E2;
}
}
}
}

View file

@ -8,7 +8,7 @@
.list {
background: #f5f5f5;
}
.slide-to-pay {
.add-bottom-for-cta {
bottom: 92px;
}
.head {
@ -23,6 +23,8 @@
img {
margin-right: 1rem;
height: 35px;
width: 35px;
}
span {
@ -243,4 +245,21 @@
color: rgba(58, 58, 58, .6);
margin-bottom: 1.5rem;
}
.glidera-success {
padding: 20px;
margin-top: 15px;
span {
font-size: 15px;
display: block;
}
}
.glidera-explanation {
padding: 0 1rem;
white-space: normal;
}
.glidera-description {
text-align: center;
font-size: 12px;
color: $mid-gray;
}
}

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;
@ -17,22 +18,24 @@
}
}
.item{
padding: calc(100vh - 99vh) calc(100vw - 93vw) calc(100vh - 97vh) calc(100vw - 95vw);
i{left:auto;}
padding: 3vh 3vw 3vh 3vw;
span{
clear:both;
width: 100%;
display: inline-block;
&.wallet-name{
margin-top:10px;
margin-bottom:5px;
font-size:13px;
font-size:16px;
width: 70%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-weight: 600;
}
&.wallet-number{
visibility: hidden;
}
&.visible{
visibility: visible !important;
visibility: visible !important;
}
}
.big-icon-svg{
@ -64,10 +67,10 @@
transform: scale(.8);
}
&.swiper-slide-prev {
left: 10vw;
left: 5vw;
}
&.swiper-slide-next {
left: -10vw;
left: -5vw;
}
}
}

Some files were not shown because too many files have changed in this diff Show more