This commit is contained in:
Kadir Sekha 2017-10-16 18:05:09 +09:00
commit a0261a6c9f
146 changed files with 16800 additions and 5578 deletions

View file

@ -14,7 +14,7 @@ angular.module('copayApp.controllers').controller('addressbookAddController', fu
$timeout(function() {
var form = addressbookForm;
if (data && form) {
data = data.replace('bitcoin:', '');
data = data.replace(/^bitcoin(cash)?:/, '');
form.address.$setViewValue(data);
form.address.$isValid = true;
form.address.$render();

View file

@ -1,11 +1,23 @@
'use strict';
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, $stateParams, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog) {
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog, bitcoreCash) {
$scope.isChromeApp = platformInfo.isChromeApp;
$scope.addressbookEntry = {};
$scope.addressbookEntry.name = $stateParams.name;
$scope.addressbookEntry.email = $stateParams.email;
$scope.addressbookEntry.address = $stateParams.address;
var coin;
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.addressbookEntry = {};
$scope.addressbookEntry.name = data.stateParams.name;
$scope.addressbookEntry.email = data.stateParams.email;
$scope.addressbookEntry.address = data.stateParams.address;
var cashAddress = bitcoreCash.Address.isValid($scope.addressbookEntry.address, 'livenet');
if (cashAddress) {
coin = 'bch';
} else {
coin = 'btc';
}
});
$scope.sendTo = function() {
$ionicHistory.removeBackView();
@ -14,7 +26,8 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
$state.transitionTo('tabs.send.amount', {
toAddress: $scope.addressbookEntry.address,
toName: $scope.addressbookEntry.name,
toEmail: $scope.addressbookEntry.email
toEmail: $scope.addressbookEntry.email,
coin: coin
});
}, 100);
};
@ -31,7 +44,7 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
}
$ionicHistory.goBack();
});
});
});
};
});

View file

@ -1,13 +1,8 @@
'use strict';
angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, configService, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) {
angular.module('copayApp.controllers').controller('addressesController', function($scope, $log, $stateParams, $state, $timeout, $ionicHistory, $ionicScrollDelegate, popupService, gettextCatalog, ongoingProcess, lodash, profileService, walletService, bwcError, platformInfo, appConfigService, txFormatService, feeService) {
var UNUSED_ADDRESS_LIMIT = 5;
var BALANCE_ADDRESS_LIMIT = 5;
var config = configService.getSync().wallet.settings;
var unitName = config.unitName;
var unitToSatoshi = config.unitToSatoshi;
var satToUnit = 1 / unitToSatoshi;
var unitDecimals = config.unitDecimals;
var withBalance, cachedWallet;
$scope.isCordova = platformInfo.isCordova;
@ -55,7 +50,7 @@ angular.module('copayApp.controllers').controller('addressesController', functio
$scope.latestWithBalance = lodash.slice(withBalance, 0, BALANCE_ADDRESS_LIMIT);
lodash.each(withBalance, function(a) {
a.balanceStr = txFormatService.formatAmount(a.amount);
a.balanceStr = txFormatService.formatAmountStr($scope.wallet.coin, a.amount);
});
$scope.viewAll = {
@ -75,11 +70,11 @@ angular.module('copayApp.controllers').controller('addressesController', functio
feeService.getFeeLevels(function(err, levels){
feeService.getFeeLevels($scope.wallet.coin, function(err, levels){
walletService.getLowUtxos($scope.wallet, levels, function(err, resp) {
if (err) return;
if (resp.allUtxos && resp.allUtxos.length) {
if (resp && resp.allUtxos && resp.allUtxos.length) {
var allSum = lodash.sum(resp.allUtxos || 0, 'satoshis');
@ -88,9 +83,9 @@ angular.module('copayApp.controllers').controller('addressesController', functio
$scope.lowWarning = resp.warning;
$scope.lowUtxosNb = resp.lowUtxos.length;
$scope.allUtxosNb = resp.allUtxos.length;
$scope.lowUtxosSum = txFormatService.formatAmountStr(lodash.sum(resp.lowUtxos || 0, 'satoshis'));
$scope.allUtxosSum = txFormatService.formatAmountStr(allSum);
$scope.minFee = txFormatService.formatAmountStr(resp.minFee || 0);
$scope.lowUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, lodash.sum(resp.lowUtxos || 0, 'satoshis'));
$scope.allUtxosSum = txFormatService.formatAmountStr($scope.wallet.coin, allSum);
$scope.minFee = txFormatService.formatAmountStr($scope.wallet.coin, resp.minFee || 0);
$scope.minFeePer = per.toFixed(2) + '%';

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo) {
angular.module('copayApp.controllers').controller('advancedSettingsController', function($scope, $log, configService, platformInfo, externalLinkService, gettextCatalog) {
var updateConfig = function() {
var config = configService.getSync();
@ -14,6 +14,7 @@ angular.module('copayApp.controllers').controller('advancedSettingsController',
$scope.hideNextSteps = {
value: config.hideNextSteps.enabled
};
};
$scope.spendUnconfirmedChange = function() {

View file

@ -9,6 +9,14 @@ angular.module('copayApp.controllers').controller('amountController', function($
var SMALL_FONT_SIZE_LIMIT = 10;
var LENGTH_EXPRESSION_LIMIT = 19;
var isNW = platformInfo.isNW;
var unitIndex = 0;
var altUnitIndex = 0;
var availableUnits = [];
var fiatCode;
var fixedUnit;
$scope.isChromeApp = platformInfo.isChromeApp;
$scope.$on('$ionicView.leave', function() {
@ -16,19 +24,100 @@ angular.module('copayApp.controllers').controller('amountController', function($
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var config = configService.getSync().wallet.settings;
function setAvailableUnits() {
availableUnits = [];
var hasBTCWallets = profileService.getWallets({
coin: 'btc'
}).length;
if (hasBTCWallets) {
availableUnits.push({
name: 'Bitcoin',
id: 'btc',
shortName: 'BTC',
});
}
var hasBCHWallets = profileService.getWallets({
coin: 'bch'
}).length;
if (hasBCHWallets) {
availableUnits.push({
name: 'Bitcoin Cash',
id: 'bch',
shortName: 'BCH',
});
};
unitIndex = 0;
if (data.stateParams.coin) {
var coins = data.stateParams.coin.split(',');
var newAvailableUnits = [];
lodash.each(coins, function(c) {
var coin = lodash.find(availableUnits, {
id: c
});
if (!coin) {
$log.warn('Could not find desired coin:' + data.stateParams.coin)
} else {
newAvailableUnits.push(coin);
}
});
if (newAvailableUnits.length > 0) {
availableUnits = newAvailableUnits;
}
}
// currency have preference
var fiatName;
if (data.stateParams.currency) {
fiatCode = data.stateParams.currency;
altUnitIndex = unitIndex
unitIndex = availableUnits.length;
} else {
fiatCode = config.alternativeIsoCode || 'USD';
fiatName = config.alternanativeName || fiatCode;
altUnitIndex = availableUnits.length;
}
availableUnits.push({
name: fiatName || fiatCode,
// TODO
id: fiatCode,
shortName: fiatCode,
isFiat: true,
});
if (data.stateParams.fixedUnit) {
fixedUnit = true;
}
};
// Go to...
_id = data.stateParams.id; // Optional (BitPay Card ID or Wallet ID)
$scope.nextStep = data.stateParams.nextStep;
$scope.currency = data.stateParams.currency;
$scope.forceCurrency = data.stateParams.forceCurrency;
$scope.showMenu = $ionicHistory.backView() && ($ionicHistory.backView().stateName == 'tabs.send' ||
$ionicHistory.backView().stateName == 'tabs.bitpayCard');
setAvailableUnits();
updateUnitUI();
$scope.showMenu = $ionicHistory.backView() && ($ionicHistory.backView().stateName == 'tabs.send' || $ionicHistory.backView().stateName == 'tabs.bitpayCard');
$scope.recipientType = data.stateParams.recipientType || null;
$scope.toAddress = data.stateParams.toAddress;
$scope.toName = data.stateParams.toName;
$scope.toEmail = data.stateParams.toEmail;
$scope.showAlternativeAmount = !!$scope.nextStep;
$scope.toColor = data.stateParams.toColor;
$scope.showSendMax = false;
@ -52,23 +141,13 @@ angular.module('copayApp.controllers').controller('amountController', function($
} else if (e.key.match(reOp)) {
$scope.pushOperator(e.key);
} else if (e.keyCode === 86) {
if (e.ctrlKey || e.metaKey)
processClipboard();
} else if (e.keyCode === 13)
$scope.finish();
if (e.ctrlKey || e.metaKey) processClipboard();
} else if (e.keyCode === 13) $scope.finish();
$timeout(function() {
$scope.$apply();
});
});
var config = configService.getSync().wallet.settings;
$scope.unitName = config.unitName;
if (data.stateParams.currency) {
$scope.alternativeIsoCode = data.stateParams.currency;
} else {
$scope.alternativeIsoCode = config.alternativeIsoCode || 'USD';
}
$scope.specificAmount = $scope.specificAlternativeAmount = '';
$scope.isCordova = platformInfo.isCordova;
unitToSatoshi = config.unitToSatoshi;
@ -114,16 +193,59 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.finish();
};
$scope.toggleAlternative = function() {
if ($scope.forceCurrency) return;
$scope.showAlternativeAmount = !$scope.showAlternativeAmount;
$scope.toggleAlternative = function() {
if ($scope.amount && isExpression($scope.amount)) {
var amount = evaluate(format($scope.amount));
$scope.globalResult = '= ' + processResult(amount);
}
};
function updateUnitUI() {
$scope.unit = availableUnits[unitIndex].shortName;
$scope.alternativeUnit = availableUnits[altUnitIndex].shortName;
processAmount();
$log.debug('Update unit coin @amount unit:' + $scope.unit + " alternativeUnit:" + $scope.alternativeUnit);
};
$scope.changeUnit = function() {
if (fixedUnit) return;
unitIndex++;
if (unitIndex >= availableUnits.length) unitIndex = 0;
if (availableUnits[unitIndex].isFiat) {
// Always return to BTC... TODO?
altUnitIndex = 0;
} else {
altUnitIndex = lodash.findIndex(availableUnits, {
isFiat: true
});
}
updateUnitUI();
};
$scope.changeAlternativeUnit = function() {
// Do nothing is fiat is not main unit
if (!availableUnits[unitIndex].isFiat) return;
var nextCoin = lodash.findIndex(availableUnits, function(x) {
if (x.isFiat) return false;
if (x.id == availableUnits[altUnitIndex].id) return false;
return true;
});
if (nextCoin >= 0) {
altUnitIndex = nextCoin;
updateUnitUI();
}
};
function checkFontSize() {
if ($scope.amount && $scope.amount.length >= SMALL_FONT_SIZE_LIMIT) $scope.smallFont = true;
else $scope.smallFont = false;
@ -132,7 +254,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.pushDigit = function(digit) {
if ($scope.amount && $scope.amount.length >= LENGTH_EXPRESSION_LIMIT) return;
if ($scope.amount.indexOf('.') > -1 && digit == '.') return;
if ($scope.showAlternativeAmount && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
if (availableUnits[unitIndex].isFiat && $scope.amount.indexOf('.') > -1 && $scope.amount[$scope.amount.indexOf('.') + 2]) return;
$scope.amount = ($scope.amount + digit).replace('..', '.');
checkFontSize();
@ -169,7 +291,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
};
$scope.resetAmount = function() {
$scope.amount = $scope.alternativeResult = $scope.amountResult = $scope.globalResult = '';
$scope.amount = $scope.alternativeAmount = $scope.globalResult = '';
$scope.allowSend = false;
checkFontSize();
};
@ -180,24 +302,39 @@ angular.module('copayApp.controllers').controller('amountController', function($
$scope.allowSend = lodash.isNumber(result) && +result > 0;
if (lodash.isNumber(result)) {
$scope.globalResult = isExpression($scope.amount) ? '= ' + processResult(result) : '';
$scope.amountResult = $filter('formatFiatAmount')(toFiat(result));
$scope.alternativeResult = txFormatService.formatAmount(fromFiat(result) * unitToSatoshi, true);
if (availableUnits[unitIndex].isFiat) {
var a = fromFiat(result);
if (a) {
$scope.alternativeAmount = txFormatService.formatAmount(a * unitToSatoshi, true);
} else {
if (result) {
$scope.alternativeAmount = 'N/A';
} else {
$scope.alternativeAmount = null;
}
$scope.allowSend = false;
}
} else {
$scope.alternativeAmount = $filter('formatFiatAmount')(toFiat(result));
}
}
};
function processResult(val) {
if ($scope.showAlternativeAmount)
return $filter('formatFiatAmount')(val);
else
return txFormatService.formatAmount(val.toFixed(unitDecimals) * unitToSatoshi, true);
if (availableUnits[unitIndex].isFiat) return $filter('formatFiatAmount')(val);
else return txFormatService.formatAmount(val.toFixed(unitDecimals) * unitToSatoshi, true);
};
function fromFiat(val) {
return parseFloat((rateService.fromFiat(val, $scope.alternativeIsoCode) * satToUnit).toFixed(unitDecimals));
return parseFloat((rateService.fromFiat(val, fiatCode, availableUnits[altUnitIndex].id) * satToUnit).toFixed(unitDecimals));
};
function toFiat(val) {
return parseFloat((rateService.toFiat(val * unitToSatoshi, $scope.alternativeIsoCode)).toFixed(2));
if (!rateService.getRate(fiatCode)) return;
return parseFloat((rateService.toFiat(val * unitToSatoshi, fiatCode, availableUnits[unitIndex].id)).toFixed(2));
};
function evaluate(val) {
@ -212,33 +349,50 @@ angular.module('copayApp.controllers').controller('amountController', function($
};
function format(val) {
if (!val) return;
var result = val.toString();
if (isOperator(lodash.last(val)))
result = result.slice(0, -1);
if (isOperator(lodash.last(val))) result = result.slice(0, -1);
return result.replace('x', '*');
};
$scope.finish = function() {
var unit = availableUnits[unitIndex];
var _amount = evaluate(format($scope.amount));
var coin = unit.id;
if (unit.isFiat) {
coin = availableUnits[altUnitIndex].id;
}
if ($scope.nextStep) {
$state.transitionTo($scope.nextStep, {
id: _id,
amount: $scope.useSendMax ? null : _amount,
currency: $scope.showAlternativeAmount ? $scope.alternativeIsoCode : $scope.unitName,
currency: unit.id.toUpperCase(),
coin: coin,
useSendMax: $scope.useSendMax
});
} else {
var amount = $scope.showAlternativeAmount ? fromFiat(_amount) : _amount;
var amount = _amount;
if (unit.isFiat) {
amount = (fromFiat(amount) * unitToSatoshi).toFixed(0);
} else {
amount = (amount * unitToSatoshi).toFixed(0);
}
$state.transitionTo('tabs.send.confirm', {
recipientType: $scope.recipientType,
toAmount: $scope.useSendMax ? null : (amount * unitToSatoshi).toFixed(0),
toAmount: amount,
toAddress: $scope.toAddress,
toName: $scope.toName,
toEmail: $scope.toEmail,
toColor: $scope.toColor,
coin: coin,
useSendMax: $scope.useSendMax
});
}

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyAmazonController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, amazonService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, gettextCatalog, txFormatService) {
var coin = 'btc';
var amount;
var currency;
var createdTx;
@ -64,7 +65,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
@ -216,8 +217,8 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
});
var initialize = function(wallet) {
var parsedAmount = txFormatService.parseAmount(amount, currency);
$scope.currencyIsoCode = parsedAmount.alternativeIsoCode;
var parsedAmount = txFormatService.parseAmount(coin, amount, currency);
$scope.currencyIsoCode = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
var dataSrc = {
amount: parsedAmount.amount,
@ -260,7 +261,7 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
invoiceUrl: invoice.url,
invoiceTime: invoice.invoiceTime
};
$scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount);
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
});
});
@ -292,7 +293,8 @@ angular.module('copayApp.controllers').controller('buyAmazonController', functio
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
hasFunds: true
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack(null, gettextCatalog.getString('No wallets available'));

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -33,6 +34,52 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
}
};
var processPaymentInfo = function() {
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var accessToken = res.accessToken;
coinbaseService.buyPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, b) {
$scope.buyPrice = b.data || null;
});
$scope.paymentMethods = [];
$scope.selectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var hasPrimary;
var pm;
for(var i = 0; i < p.data.length; i++) {
pm = p.data[i];
if (pm.allow_buy) {
$scope.paymentMethods.push(pm);
}
if (pm.allow_buy && pm.primary_buy) {
hasPrimary = true;
$scope.selectedPaymentMethodId.value = pm.id;
}
}
if (lodash.isEmpty($scope.paymentMethods)) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack('No payment method available to buy');
return;
}
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
$scope.buyRequest();
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
@ -42,81 +89,22 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
// Buy always in BTC
amount = (parsedAmount.amountSat / 100000000).toFixed(8);
currency = 'BTC';
$scope.amountUnitStr = parsedAmount.amountUnitStr;
ongoingProcess.set('calculatingFee', true);
coinbaseService.checkEnoughFundsForFee(amount, function(err) {
ongoingProcess.set('calculatingFee', false);
if (err) {
showErrorAndBack(err);
return;
}
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var accessToken = res.accessToken;
coinbaseService.buyPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, b) {
$scope.buyPrice = b.data || null;
});
$scope.paymentMethods = [];
$scope.selectedPaymentMethodId = { value : null };
coinbaseService.getPaymentMethods(accessToken, function(err, p) {
if (err) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack(err);
return;
}
var hasPrimary;
var pm;
for(var i = 0; i < p.data.length; i++) {
pm = p.data[i];
if (pm.allow_buy) {
$scope.paymentMethods.push(pm);
}
if (pm.allow_buy && pm.primary_buy) {
hasPrimary = true;
$scope.selectedPaymentMethodId.value = pm.id;
}
}
if (lodash.isEmpty($scope.paymentMethods)) {
ongoingProcess.set('connectingCoinbase', false);
showErrorAndBack('No payment method available to buy');
return;
}
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
$scope.buyRequest();
});
});
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.buyRequest = function() {
@ -209,21 +197,31 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
});
};
var _processBuyOrder = function() {
coinbaseService.getBuyOrder(accessToken, accountId, b.data.id, function (err, buyResp) {
if (err) {
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
showError(err);
return;
}
var tx = buyResp.data ? buyResp.data.transaction : null;
if (tx && tx.id) {
processBuyTx(tx);
} else {
$timeout(function() {
_processBuyOrder();
}, 5000);
}
});
}
$timeout(function() {
var tx = b.data ? b.data.transaction : null;
if (tx) {
if (tx && tx.id) {
processBuyTx(tx);
}
else {
coinbaseService.getBuyOrder(accessToken, accountId, b.data.id, function (err, buyResp) {
if (err) {
ongoingProcess.set('buyingBitcoin', false, statusChangeHandler);
showError(err);
return;
}
var tx = buyResp.data ? buyResp.data.transaction : null;
processBuyTx(tx);
});
_processBuyOrder();
}
}, 8000);
});
@ -238,6 +236,25 @@ angular.module('copayApp.controllers').controller('buyCoinbaseController', funct
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
// Buy always in BTC
amount = (parsedAmount.amountSat / 100000000).toFixed(8);
currency = 'BTC';
$scope.amountUnitStr = parsedAmount.amountUnitStr;
ongoingProcess.set('calculatingFee', true);
coinbaseService.checkEnoughFundsForFee(amount, function(err) {
ongoingProcess.set('calculatingFee', false);
if (err) {
showErrorAndBack(err);
return;
}
processPaymentInfo();
});
};
$scope.goBackHome = function() {

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('buyGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, platformInfo, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -35,36 +36,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingGlidera', true);
glideraService.init(function(err, data) {
if (err) {
@ -88,6 +60,33 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
$scope.buyInfo = buy;
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('No wallets available');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
var ask2FaCode = function(mode, cb) {
@ -105,7 +104,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
popupService.showPrompt(title, message, null, function(twoFaCode) {
if (typeof twoFaCode == 'undefined') return cb();
return cb(twoFaCode);
});
});
} else {
return cb();
}
@ -116,7 +115,7 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
var okText = 'Confirm';
var cancelText = 'Cancel';
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
if (!ok) return;
if (!ok) return;
ongoingProcess.set('buyingBitcoin', true, statusChangeHandler);
glideraService.get2faCode($scope.token, function(err, tfa) {
if (err) {
@ -162,6 +161,15 @@ angular.module('copayApp.controllers').controller('buyGlideraController', functi
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -0,0 +1,354 @@
'use strict';
angular.module('copayApp.controllers').controller('buyMercadoLibreController', function($scope, $log, $state, $timeout, $filter, $ionicHistory, $ionicConfig, lodash, mercadoLibreService, popupService, profileService, ongoingProcess, configService, walletService, payproService, bwcError, externalLinkService, platformInfo, txFormatService, gettextCatalog) {
var coin = 'btc';
var amount;
var currency;
var createdTx;
var message;
var invoiceId;
var configWallet = configService.getSync().wallet;
$scope.isCordova = platformInfo.isCordova;
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var _resetValues = function() {
$scope.totalAmountStr = $scope.amount = $scope.invoiceFee = $scope.networkFee = $scope.totalAmount = $scope.wallet = null;
createdTx = message = invoiceId = null;
};
var showErrorAndBack = function(title, msg) {
title = title || gettextCatalog.getString('Error');
$scope.sendStatus = '';
$log.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
popupService.showAlert(title, msg, function() {
$ionicHistory.goBack();
});
};
var showError = function(title, msg, cb) {
cb = cb || function() {};
title = title || gettextCatalog.getString('Error');
$scope.sendStatus = '';
$log.error(msg);
msg = (msg && msg.errors) ? msg.errors[0].message : msg;
popupService.showAlert(title, msg, cb);
};
var publishAndSign = function(wallet, txp, onSendStatusChange, cb) {
if (!wallet.canSign() && !wallet.isPrivKeyExternal()) {
var err = 'No signing proposal: No private key';
$log.info(err);
return cb(err);
}
walletService.publishAndSign(wallet, txp, function(err, txp) {
if (err) return cb(err);
return cb(null, txp);
}, onSendStatusChange);
};
var statusChangeHandler = function(processName, showName, isOn) {
$log.debug('statusChangeHandler: ', processName, showName, isOn);
if (processName == 'Comprando Vale-Presente' && !isOn) {
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
}, 100);
} else if (showName) {
$scope.sendStatus = showName;
}
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
var setTotalAmount = function(amountSat, invoiceFeeSat, networkFeeSat) {
satToFiat(amountSat, function(a) {
$scope.amount = Number(a);
satToFiat(invoiceFeeSat, function(i) {
$scope.invoiceFee = Number(i);
satToFiat(networkFeeSat, function(n) {
$scope.networkFee = Number(n);
$scope.totalAmount = $scope.amount + $scope.invoiceFee + $scope.networkFee;
$timeout(function() {
$scope.$digest();
});
});
});
});
};
var createInvoice = function(data, cb) {
mercadoLibreService.createBitPayInvoice(data, function(err, dataInvoice) {
if (err) {
var err_title = gettextCatalog.getString('Error creating the invoice');
var err_msg;
if (err && err.message && err.message.match(/suspended/i)) {
err_title = gettextCatalog.getString('Service not available');
err_msg = gettextCatalog.getString('Mercadolibre Gift Card Service is not available at this moment. Please try back later.');
} else if (err && err.message) {
err_msg = err.message;
} else {
err_msg = gettextCatalog.getString('Could not access Gift Card Service');
};
return cb({
title: err_title,
message: err_msg
});
}
var accessKey = dataInvoice ? dataInvoice.accessKey : null;
if (!accessKey) {
return cb({
message: gettextCatalog.getString('No access key defined')
});
}
mercadoLibreService.getBitPayInvoice(dataInvoice.invoiceId, function(err, invoice) {
if (err) {
return cb({
message: gettextCatalog.getString('Could not get the invoice')
});
}
return cb(null, invoice, accessKey);
});
});
};
var createTx = function(wallet, invoice, message, cb) {
var payProUrl = (invoice && invoice.paymentUrls) ? invoice.paymentUrls.BIP73 : null;
if (!payProUrl) {
return cb({
title: gettextCatalog.getString('Error in Payment Protocol'),
message: gettextCatalog.getString('Invalid URL')
});
}
var outputs = [];
var toAddress = invoice.bitcoinAddress;
var amountSat = parseInt((invoice.btcDue * 100000000).toFixed(0)); // BTC to Satoshi
outputs.push({
'toAddress': toAddress,
'amount': amountSat,
'message': message
});
var txp = {
toAddress: toAddress,
amount: amountSat,
outputs: outputs,
message: message,
payProUrl: payProUrl,
excludeUnconfirmedUtxos: configWallet.spendUnconfirmed ? false : true,
feeLevel: configWallet.settings.feeLevel || 'normal'
};
walletService.createTx(wallet, txp, function(err, ctxp) {
if (err) {
return cb({
title: gettextCatalog.getString('Could not create transaction'),
message: bwcError.msg(err)
});
}
return cb(null, ctxp);
});
};
var checkTransaction = lodash.throttle(function(count, dataSrc) {
mercadoLibreService.createGiftCard(dataSrc, function(err, giftCard) {
$log.debug("creating gift card " + count);
if (err) {
$scope.sendStatus = '';
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
giftCard = {};
giftCard.status = 'FAILURE';
}
if (giftCard && giftCard.cardStatus && (giftCard.cardStatus != 'active' && giftCard.cardStatus != 'inactive' && giftCard.cardStatus != 'expired')) {
$scope.sendStatus = '';
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
giftCard = {};
giftCard.status = 'FAILURE';
}
if (giftCard.status == 'PENDING' && count < 3) {
$log.debug("Waiting for payment confirmation");
checkTransaction(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['currency'] = dataSrc.currency;
newData['date'] = dataSrc.invoiceTime || now;
newData['uuid'] = dataSrc.uuid;
mercadoLibreService.savePendingGiftCard(newData, null, function(err) {
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
$log.debug("Saving new gift card with status: " + newData.status);
$scope.mlGiftCard = newData;
});
});
}, 8000, {
'leading': true
});
var initialize = function(wallet) {
var parsedAmount = txFormatService.parseAmount(coin, amount, currency);
$scope.currencyIsoCode = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
var dataSrc = {
amount: parsedAmount.amount,
currency: parsedAmount.currency,
uuid: wallet.id
};
ongoingProcess.set('loadingTxInfo', true);
createInvoice(dataSrc, function(err, invoice, accessKey) {
if (err) {
ongoingProcess.set('loadingTxInfo', false);
showErrorAndBack(err.title, err.message);
return;
}
// Sometimes API does not return this element;
invoice['buyerPaidBtcMinerFee'] = invoice.buyerPaidBtcMinerFee || 0;
var invoiceFeeSat = (invoice.buyerPaidBtcMinerFee * 100000000).toFixed();
message = gettextCatalog.getString("{{amountStr}} for Mercado Livre Brazil Gift Card", {
amountStr: $scope.amountUnitStr
});
createTx(wallet, invoice, message, function(err, ctxp) {
ongoingProcess.set('loadingTxInfo', false);
if (err) {
_resetValues();
showError(err.title, err.message);
return;
}
// Save in memory
createdTx = ctxp;
invoiceId = invoice.id;
createdTx['giftData'] = {
currency: dataSrc.currency,
amount: dataSrc.amount,
uuid: dataSrc.uuid,
accessKey: accessKey,
invoiceId: invoice.id,
invoiceUrl: invoice.url,
invoiceTime: invoice.invoiceTime
};
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
amount = data.stateParams.amount;
currency = data.stateParams.currency;
if (amount > 2000 || amount < 50) {
showErrorAndBack(null, gettextCatalog.getString('Purchase amount must be a value between 50 and 2000'));
return;
}
$scope.network = mercadoLibreService.getNetwork();
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: $scope.network,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack(null, gettextCatalog.getString('No wallets available'));
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.buyConfirm = function() {
if (!createdTx) {
showError(null, gettextCatalog.getString('Transaction has not been created'));
return;
}
var title = gettextCatalog.getString('Confirm');
var okText = gettextCatalog.getString('Ok');
var cancelText = gettextCatalog.getString('Cancel');
popupService.showConfirm(title, message, okText, cancelText, function(ok) {
if (!ok) {
$scope.sendStatus = '';
return;
}
ongoingProcess.set('Comprando Vale-Presente', true, statusChangeHandler);
publishAndSign($scope.wallet, createdTx, function() {}, function(err, txSent) {
if (err) {
ongoingProcess.set('Comprando Vale-Presente', false, statusChangeHandler);
showError(gettextCatalog.getString('Could not send transaction'), err);
return;
}
checkTransaction(1, createdTx.giftData);
});
});
};
$scope.showWalletSelector = function() {
$scope.walletSelectorTitle = 'Buy from';
$scope.showWallets = true;
};
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
initialize(wallet);
};
$scope.goBackHome = function() {
$scope.sendStatus = '';
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.home').then(function() {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.transitionTo('tabs.giftcards.mercadoLibre').then(function() {
$state.transitionTo('tabs.giftcards.mercadoLibre.cards', {
invoiceId: invoiceId
});
});
});
};
});

View file

@ -0,0 +1,192 @@
'use strict';
angular.module('copayApp.controllers').controller('cashScanController',
function($rootScope, $timeout, $scope, $state, $ionicHistory, gettextCatalog, lodash, ongoingProcess, profileService, walletService, $log, txFormatService, bwcError, pushNotificationsService, bwcService, externalLinkService) {
var wallet;
var errors = bwcService.getErrors();
$scope.error = null;
$scope.walletDisabled = '#667';
$scope.$on("$ionicView.beforeEnter", function(event, data) {
updateAllWallets();
});
$scope.openRecoveryToolLink = function() {
var url = 'https://bitpay.github.io/copay-recovery/';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Open the recovery tool.');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
var goHome = function() {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$ionicHistory.clearHistory();
$state.go('tabs.settings').then(function() {
$state.transitionTo('tabs.home');
});
}
var updateAllWallets = function() {
var walletsBTC = profileService.getWallets({
coin: 'btc',
onlyComplete: true,
network: 'livenet'
});
// Filter out already duplicated wallets
var walletsBCH = profileService.getWallets({
coin: 'bch',
network: 'livenet'
});
var xPubKeyIndex = lodash.indexBy(walletsBCH, "credentials.xPubKey");
walletsBTC = lodash.filter(walletsBTC, function(w) {
return !xPubKeyIndex[w.credentials.xPubKey];
});
var availableWallets = [];
var nonEligibleWallets = [];
lodash.each(walletsBTC, function(w) {
if (w.credentials.derivationStrategy != 'BIP44') {
w.excludeReason = gettextCatalog.getString('Non BIP44 wallet');
nonEligibleWallets.push(w);
} else if (!w.canSign()) {
w.excludeReason = gettextCatalog.getString('Read only wallet');
nonEligibleWallets.push(w);
} else if (w.needsBackup) {
w.excludeReason = gettextCatalog.getString('Backup needed');
nonEligibleWallets.push(w);
} else {
availableWallets.push(w);
}
});
$scope.availableWallets = availableWallets;
$scope.nonEligibleWallets = nonEligibleWallets;
var i = availableWallets.length;
var j = 0;
lodash.each(availableWallets, function(wallet) {
walletService.getBalance(wallet, {
coin: 'bch'
}, function(err, balance) {
if (err) {
wallet.error = (err === 'WALLET_NOT_REGISTERED') ? gettextCatalog.getString('Wallet not registered') : bwcError.msg(err);
$log.error(err);
return;
}
wallet.error = null;
wallet.bchBalance = txFormatService.formatAmountStr('bch', balance.availableAmount);
if (++j == i) {
//Done
$timeout(function() {
$rootScope.$apply();
}, 10);
}
});
});
};
$scope.duplicate = function(wallet) {
$scope.error = null;
$log.debug('Duplicating wallet for BCH:' + wallet.id + ':' + wallet.name);
var opts = {};
opts.name = wallet.name + '[BCH]';
opts.m = wallet.m;
opts.n = wallet.n;
opts.myName = wallet.credentials.copayerName;
opts.networkName = wallet.network;
opts.coin = 'bch';
opts.walletPrivKey = wallet.credentials.walletPrivKey;
opts.compliantDerivation = wallet.credentials.compliantDerivation;
function setErr(err, cb) {
if (!cb) cb = function() {};
$scope.error = bwcError.cb(err, gettextCatalog.getString('Could not duplicate'), function() {
return cb(err);
});
$timeout(function() {
$rootScope.$apply();
}, 10);
}
function importOrCreate(cb) {
walletService.getStatus(wallet, {}, function(err, status) {
if (err) return cb(err);
opts.singleAddress = status.wallet.singleAddress;
// first try to import
profileService.importExtendedPrivateKey(opts.extendedPrivateKey, opts, function(err, newWallet) {
if (err && !(err instanceof errors.NOT_AUTHORIZED)) {
return setErr(err, cb);
}
if (err) {
// create and store a wallet
return profileService.createWallet(opts, function(err, newWallet) {
if (err) return setErr(err, cb);
return cb(null, newWallet, true);
});
}
return cb(null, newWallet);
});
});
};
// Multisig wallets? add Copayers
function addCopayers(newWallet, isNew, cb) {
if (!isNew) return cb();
if (wallet.n == 1) return cb();
$log.info('Adding copayers for BCH wallet config:' + wallet.m + '-' + wallet.n);
walletService.copyCopayers(wallet, newWallet, function(err) {
if (err) return setErr(err, cb);
return cb();
});
};
walletService.getKeys(wallet, function(err, keys) {
if (err) {
$scope.error = err;
return $timeout(function() {
$rootScope.$apply();
}, 10);
}
opts.extendedPrivateKey = keys.xPrivKey;
ongoingProcess.set('duplicatingWallet', true);
importOrCreate(function(err, newWallet, isNew) {
if (err) {
ongoingProcess.set('duplicatingWallet', false);
return;
}
walletService.updateRemotePreferences(newWallet);
pushNotificationsService.updateSubscription(newWallet);
addCopayers(newWallet, isNew, function(err) {
ongoingProcess.set('duplicatingWallet', false);
if (err)
return setErr(err);
if (isNew)
walletService.startScan(newWallet, function() {});
goHome();
});
});
});
}
});

View file

@ -1,6 +1,6 @@
'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, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification) {
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $filter, $timeout, $ionicScrollDelegate, gettextCatalog, walletService, platformInfo, lodash, configService, $stateParams, $window, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, payproService, feeService, bwcError, txConfirmNotification, externalLinkService) {
var countDown = null;
var CONFIRM_LIMIT_USD = 20;
@ -28,7 +28,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
function refresh() {
$timeout(function() {
$scope.$apply();
}, 1);
}, 10);
}
@ -58,9 +58,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
});
};
function setNoWallet(msg) {
function setNoWallet(msg, criticalError) {
$scope.wallet = null;
$scope.noWalletMessage = msg;
$scope.criticalError = criticalError;
$log.warn('Not ready to make the payment:' + msg);
$timeout(function() {
$scope.$apply();
@ -69,18 +70,19 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.$on("$ionicView.beforeEnter", function(event, data) {
function setWalletSelector(network, minAmount, cb) {
function setWalletSelector(coin, network, minAmount, cb) {
// no min amount? (sendMax) => look for no empty wallets
minAmount = minAmount || 1;
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: network
network: network,
coin: coin
});
if (!$scope.wallets || !$scope.wallets.length) {
setNoWallet(gettextCatalog.getString('No wallets available'));
setNoWallet(gettextCatalog.getString('No wallets available'), true);
return cb();
}
@ -109,7 +111,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
return cb('Could not update any wallet');
if (lodash.isEmpty(filteredWallets)) {
setNoWallet(gettextCatalog.getString('Insufficient funds'));
setNoWallet(gettextCatalog.getString('Insufficient funds'), true);
}
$scope.wallets = lodash.clone(filteredWallets);
return cb();
@ -120,6 +122,30 @@ angular.module('copayApp.controllers').controller('confirmController', function(
// Setup $scope
var B = data.stateParams.coin == 'bch' ? bitcoreCash : bitcore;
var networkName;
try {
networkName = (new B.Address(data.stateParams.toAddress)).network.name;
} catch(e) {
var message = gettextCatalog.getString('Copay only supports Bitcoin Cash using new version numbers addresses');
var backText = gettextCatalog.getString('Go back');
var learnText = gettextCatalog.getString('Learn more');
popupService.showConfirm(null, message, backText, learnText, function(back) {
$ionicHistory.nextViewOptions({
disableAnimate: true,
historyRoot: true
});
$state.go('tabs.send').then(function() {
$ionicHistory.clearHistory();
if (!back) {
var url = 'https://support.bitpay.com/hc/en-us/articles/115004671663';
externalLinkService.open(url);
}
});
});
return;
}
// Grab stateParams
tx = {
toAmount: parseInt(data.stateParams.toAmount),
@ -136,33 +162,32 @@ angular.module('copayApp.controllers').controller('confirmController', function(
toName: data.stateParams.toName,
toEmail: data.stateParams.toEmail,
toColor: data.stateParams.toColor,
network: (new bitcore.Address(data.stateParams.toAddress)).network.name,
network: networkName,
coin: data.stateParams.coin,
txp: {},
};
if (tx.coin && tx.coin == 'bch') tx.feeLevel = 'normal';
// Other Scope vars
$scope.isCordova = isCordova;
$scope.isWindowsPhoneApp = isWindowsPhoneApp;
$scope.showAddress = false;
updateTx(tx, null, {}, function() {
$scope.walletSelectorTitle = gettextCatalog.getString('Send from');
$scope.walletSelectorTitle = gettextCatalog.getString('Send from');
setWalletSelector(tx.network, tx.toAmount, function(err) {
if (err) {
return exitWithError('Could not update wallets');
}
if ($scope.wallets.length > 1) {
$scope.showWalletSelector();
} else if ($scope.wallets.length) {
setWallet($scope.wallets[0], tx);
}
});
setWalletSelector(tx.coin, tx.network, tx.toAmount, function(err) {
if (err) {
return exitWithError('Could not update wallets');
}
if ($scope.wallets.length > 1) {
$scope.showWalletSelector();
} else if ($scope.wallets.length) {
setWallet($scope.wallets[0], tx);
}
});
});
@ -227,6 +252,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
function updateTx(tx, wallet, opts, cb) {
ongoingProcess.set('calculatingFee', true);
if (opts.clearCache) {
tx.txp = {};
@ -238,10 +264,10 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (!tx.toAmount) return;
// Amount
tx.amountStr = txFormatService.formatAmountStr(tx.toAmount);
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.toAmount);
tx.amountValueStr = tx.amountStr.split(' ')[0];
tx.amountUnitStr = tx.amountStr.split(' ')[1];
txFormatService.formatAlternativeStr(tx.toAmount, function(v) {
txFormatService.formatAlternativeStr(wallet.coin, tx.toAmount, function(v) {
tx.alternativeAmountStr = v;
});
}
@ -250,19 +276,23 @@ angular.module('copayApp.controllers').controller('confirmController', function(
refresh();
// End of quick refresh, before wallet is selected.
if (!wallet) return cb();
if (!wallet) {
ongoingProcess.set('calculatingFee', false);
return cb();
}
feeService.getFeeRate(tx.network, tx.feeLevel, function(err, feeRate) {
if (err) return cb(err);
feeService.getFeeRate(wallet.coin, tx.network, tx.feeLevel, function(err, feeRate) {
if (err) {
ongoingProcess.set('calculatingFee', false);
return cb(err);
}
if (!usingCustomFee) tx.feeRate = feeRate;
tx.feeLevelName = feeService.feeOpts[tx.feeLevel];
if (!wallet)
return cb();
getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) {
if (err) {
ongoingProcess.set('calculatingFee', false);
var msg = gettextCatalog.getString('Error getting SendMax information');
return setSendError(msg);
}
@ -272,6 +302,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$log.debug('Send max info', sendMaxInfo);
if (tx.sendMax && sendMaxInfo.amount == 0) {
ongoingProcess.set('calculatingFee', false);
setNoWallet(gettextCatalog.getString('Insufficient funds'));
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'));
return cb('no_funds');
@ -280,20 +311,27 @@ angular.module('copayApp.controllers').controller('confirmController', function(
tx.sendMaxInfo = sendMaxInfo;
tx.toAmount = tx.sendMaxInfo.amount;
updateAmount();
showSendMaxWarning(sendMaxInfo);
ongoingProcess.set('calculatingFee', false);
$timeout(function() {
showSendMaxWarning(wallet, sendMaxInfo);
}, 200);
}
// txp already generated for this wallet?
if (tx.txp[wallet.id]) {
ongoingProcess.set('calculatingFee', false);
refresh();
return cb();
}
getTxp(lodash.clone(tx), wallet, opts.dryRun, function(err, txp) {
if (err) return cb(err);
ongoingProcess.set('calculatingFee', false);
if (err) {
return cb(err);
}
txp.feeStr = txFormatService.formatAmountStr(txp.fee);
txFormatService.formatAlternativeStr(txp.fee, function(v) {
txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee);
txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) {
txp.alternativeFeeStr = v;
});
@ -321,14 +359,26 @@ angular.module('copayApp.controllers').controller('confirmController', function(
}
function setButtonText(isMultisig, isPayPro) {
$scope.buttonText = gettextCatalog.getString(isCordova && !isWindowsPhoneApp ? 'Slide' : 'Click') + ' ';
if (isPayPro) {
$scope.buttonText += gettextCatalog.getString('to pay');
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to pay');
} else {
$scope.buttonText = gettextCatalog.getString('Click to pay');
}
} else if (isMultisig) {
$scope.buttonText += gettextCatalog.getString('to accept');
} else
$scope.buttonText += gettextCatalog.getString('to send');
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to accept');
} else {
$scope.buttonText = gettextCatalog.getString('Click to accept');
}
} else {
if (isCordova && !isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to send');
} else {
$scope.buttonText = gettextCatalog.getString('Click to send');
}
}
};
@ -337,26 +387,26 @@ angular.module('copayApp.controllers').controller('confirmController', function(
};
function showSendMaxWarning(sendMaxInfo) {
function showSendMaxWarning(wallet, sendMaxInfo) {
function verifyExcludedUtxos() {
var warningMsg = [];
if (sendMaxInfo.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(sendMaxInfo.amountBelowFee)
amountBelowFeeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountBelowFee)
}));
}
if (sendMaxInfo.utxosAboveMaxSize > 0) {
warningMsg.push(gettextCatalog.getString("A total of {{amountAboveMaxSizeStr}} were excluded. The maximum size allowed for a transaction was exceeded.", {
amountAboveMaxSizeStr: txFormatService.formatAmountStr(sendMaxInfo.amountAboveMaxSize)
amountAboveMaxSizeStr: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.amountAboveMaxSize)
}));
}
return warningMsg.join('\n');
};
var msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees.", {
fee: txFormatService.formatAmountStr(sendMaxInfo.fee)
fee: txFormatService.formatAmountStr(wallet.coin, sendMaxInfo.fee)
});
var warningMsg = verifyExcludedUtxos();
@ -422,6 +472,11 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.wallet = wallet;
// If select another wallet
tx.coin = wallet.coin;
tx.feeLevel = wallet.coin == 'bch' ? 'normal' : configFeeLevel;
usingCustomFee = null;
setButtonText(wallet.credentials.m > 1, !!tx.paypro);
if (tx.paypro)
@ -483,7 +538,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
if (walletService.isEncrypted(wallet))
return cb();
var amountUsd = parseFloat(txFormatService.formatToUSD(txp.amount));
var amountUsd = parseFloat(txFormatService.formatToUSD(wallet.coin, txp.amount));
if (amountUsd <= CONFIRM_LIMIT_USD)
return cb();
@ -563,10 +618,13 @@ angular.module('copayApp.controllers').controller('confirmController', function(
$scope.chooseFeeLevel = function(tx, wallet) {
if (wallet.coin == 'bch') return;
var scope = $rootScope.$new(true);
scope.network = tx.network;
scope.feeLevel = tx.feeLevel;
scope.noSave = true;
scope.coin = wallet.coin;
if (usingCustomFee) {
scope.customFeePerKB = tx.feeRate;

View file

@ -22,11 +22,16 @@ angular.module('copayApp.controllers').controller('createController',
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.formData = {};
var defaults = configService.getDefaults();
var config = configService.getSync();
var tc = $state.current.name == 'tabs.add.create-personal' ? 1 : defaults.wallet.totalCopayers;
$scope.formData.account = 1;
$scope.formData.bwsurl = defaults.bws.url;
$scope.TCValues = lodash.range(2, defaults.limits.totalCopayers + 1);
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.coin = data.stateParams.coin;
if (config.cashSupport) $scope.enableCash = true;
$scope.setTotalCopayers(tc);
updateRCSelect(tc);
resetPasswordFields();
@ -133,10 +138,11 @@ angular.module('copayApp.controllers').controller('createController',
m: $scope.formData.requiredCopayers,
n: $scope.formData.totalCopayers,
myName: $scope.formData.totalCopayers > 1 ? $scope.formData.myName : null,
networkName: $scope.formData.testnetEnabled ? 'testnet' : 'livenet',
networkName: $scope.formData.testnetEnabled && $scope.formData.coin != 'bch' ? 'testnet' : 'livenet',
bwsurl: $scope.formData.bwsurl,
singleAddress: $scope.formData.singleAddressEnabled,
walletPrivKey: $scope.formData._walletPrivKey, // Only for testing
coin: $scope.formData.coin
};
var setSeed = $scope.formData.seedSource.id == 'set';
@ -170,6 +176,11 @@ angular.module('copayApp.controllers').controller('createController',
}
if ($scope.formData.seedSource.id == walletService.externalSource.ledger.id || $scope.formData.seedSource.id == walletService.externalSource.trezor.id || $scope.formData.seedSource.id == walletService.externalSource.intelTEE.id) {
if ($scope.formData.coin == 'bch') {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Hardware wallets are not yet supported with Bitcoin Cash'));
return;
}
var account = $scope.formData.account;
if (!account || account < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));

View file

@ -8,6 +8,10 @@ angular.module('copayApp.controllers').controller('customAmountController', func
});
};
var setProtocolHandler = function() {
$scope.protocolHandler = walletService.getProtocolHandler($scope.wallet);
}
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var walletId = data.stateParams.id;
@ -15,21 +19,25 @@ angular.module('copayApp.controllers').controller('customAmountController', func
showErrorAndBack('Error', 'No wallet selected');
return;
}
$scope.showShareButton = platformInfo.isCordova ? (platformInfo.isIOS ? 'iOS' : 'Android') : null;
$scope.wallet = profileService.getWallet(walletId);
setProtocolHandler();
walletService.getAddress($scope.wallet, false, function(err, addr) {
if (!addr) {
showErrorAndBack('Error', 'Could not get the address');
return;
}
$scope.address = addr;
$scope.coin = data.stateParams.coin;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
$scope.wallet.coin,
data.stateParams.amount,
data.stateParams.currency);
// Amount in USD or BTC
@ -37,17 +45,17 @@ angular.module('copayApp.controllers').controller('customAmountController', func
var currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
if (currency != 'BTC') {
// Convert to BTC
if (currency != 'BTC' && currency != 'BCH') {
// Convert to BTC or BCH
var config = configService.getSync().wallet.settings;
var amountUnit = txFormatService.satToUnit(parsedAmount.amountSat);
var btcParsedAmount = txFormatService.parseAmount(amountUnit, config.unitName);
var btcParsedAmount = txFormatService.parseAmount($scope.wallet.coin, amountUnit, $scope.wallet.coin);
$scope.amountBtc = btcParsedAmount.amount;
$scope.altAmountStr = btcParsedAmount.amountUnitStr;
} else {
$scope.amountBtc = amount; // BTC
$scope.altAmountStr = txFormatService.formatAlternativeStr(parsedAmount.amountSat);
$scope.amountBtc = amount; // BTC or BCH
$scope.altAmountStr = txFormatService.formatAlternativeStr($scope.wallet.coin, parsedAmount.amountSat);
}
});
});
@ -61,12 +69,16 @@ angular.module('copayApp.controllers').controller('customAmountController', func
$scope.shareAddress = function() {
if (!platformInfo.isCordova) return;
var data = 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc;
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
var data = protocol + ':' + $scope.address + '?amount=' + $scope.amountBtc;
window.plugins.socialsharing.share(data, null, null, null);
}
$scope.copyToClipboard = function() {
return 'bitcoin:' + $scope.address + '?amount=' + $scope.amountBtc;
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
return protocol + ':' + $scope.address + '?amount=' + $scope.amountBtc;
};
});

View file

@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('importController',
var reader = new FileReader();
var defaults = configService.getDefaults();
var config = configService.getSync();
var errors = bwcService.getErrors();
$scope.init = function() {
@ -15,9 +16,14 @@ angular.module('copayApp.controllers').controller('importController',
$scope.formData.bwsurl = defaults.bws.url;
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.account = 1;
$scope.formData.coin = $stateParams.coin;
$scope.importErr = false;
$scope.isCopay = appConfigService.name == 'copay';
$scope.fromHardwareWallet = { value: false };
$scope.fromHardwareWallet = {
value: false
};
if (config.cashSupport) $scope.enableCash = true;
if ($stateParams.code)
$scope.processWalletInfo($stateParams.code);
@ -59,6 +65,15 @@ angular.module('copayApp.controllers').controller('importController',
});
};
$scope.switchTestnetOff = function() {
$scope.formData.testnetEnabled = false;
$scope.setDerivationPath();
$scope.resizeView();
$timeout(function() {
$scope.$apply();
});
};
$scope.processWalletInfo = function(code) {
if (!code) return;
@ -203,6 +218,7 @@ angular.module('copayApp.controllers').controller('importController',
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
var opts = {};
opts.bwsurl = $scope.formData.bwsurl;
opts.coin = $scope.formData.coin;
_importBlob(evt.target.result, opts);
}
}
@ -228,6 +244,7 @@ angular.module('copayApp.controllers').controller('importController',
} else {
var opts = {};
opts.bwsurl = $scope.formData.bwsurl;
opts.coin = $scope.formData.coin;
_importBlob(backupText, opts);
}
};
@ -253,6 +270,7 @@ angular.module('copayApp.controllers').controller('importController',
opts.account = pathData.account;
opts.networkName = pathData.networkName;
opts.derivationStrategy = pathData.derivationStrategy;
opts.coin = $scope.formData.coin;
var words = $scope.formData.words || null;
@ -279,7 +297,7 @@ angular.module('copayApp.controllers').controller('importController',
$log.warn('This wont work for Intel TEE wallets');
var id = $scope.formData.seedSourceAll.id;
var isMultisig = opts.derivationStrategy =='BIP48';
var isMultisig = opts.derivationStrategy == 'BIP48';
var account = opts.account;
opts.entropySourcePath = 'm/' + hwWallet.getEntropyPath(id, isMultisig, account);
}

View file

@ -5,11 +5,14 @@ angular.module('copayApp.controllers').controller('joinController',
$scope.$on("$ionicView.beforeEnter", function(event, data) {
var defaults = configService.getDefaults();
var config = configService.getSync();
$scope.formData = {};
$scope.formData.bwsurl = defaults.bws.url;
$scope.formData.derivationPath = derivationPathHelper.default;
$scope.formData.account = 1;
$scope.formData.secret = null;
$scope.formData.coin = data.stateParams.coin;
if (config.cashSupport) $scope.enableCash = true;
resetPasswordFields();
updateSeedSourceSelect();
});
@ -103,7 +106,8 @@ angular.module('copayApp.controllers').controller('joinController',
var opts = {
secret: $scope.formData.secret,
myName: $scope.formData.myName,
bwsurl: $scope.formData.bwsurl
bwsurl: $scope.formData.bwsurl,
coin: $scope.formData.coin
}
var setSeed = $scope.formData.seedSource.id == 'set';
@ -137,6 +141,11 @@ angular.module('copayApp.controllers').controller('joinController',
}
if ($scope.formData.seedSource.id == walletService.externalSource.ledger.id || $scope.formData.seedSource.id == walletService.externalSource.trezor.id || $scope.formData.seedSource.id == walletService.externalSource.intelTEE.id) {
if ($scope.formData.coin == 'bch') {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Hardware wallets are not yet supported with Bitcoin Cash'));
return;
}
var account = $scope.formData.account;
if (!account || account < 1) {
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Invalid account number'));

View file

@ -0,0 +1,24 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreController',
function($scope, $timeout, $log, mercadoLibreService, externalLinkService, popupService) {
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var init = function() {
mercadoLibreService.getPendingGiftCards(function(err, gcds) {
if (err) $log.error(err);
$scope.giftCards = gcds;
$timeout(function() {
$scope.$digest();
});
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.network = mercadoLibreService.getNetwork();
init();
});
});

View file

@ -0,0 +1,100 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreCardsController',
function($scope, $timeout, $ionicModal, $log, $ionicScrollDelegate, lodash, mercadoLibreService, platformInfo, externalLinkService, popupService, ongoingProcess) {
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
var updateGiftCards = function(cb) {
mercadoLibreService.getPendingGiftCards(function(err, gcds) {
if (err) {
popupService.showAlert('Could not get gift cards', err);
if (cb) return cb();
else return;
}
$scope.giftCards = gcds;
$timeout(function() {
$scope.$digest();
$ionicScrollDelegate.resize();
if (cb) return cb();
}, 100);
});
};
$scope.updatePendingGiftCards = lodash.debounce(function() {
updateGiftCards(function() {
var index = 0;
var gcds = $scope.giftCards;
lodash.forEach(gcds, function(dataFromStorage) {
if (dataFromStorage.status == 'PENDING') {
$log.debug("Creating / Updating gift card");
mercadoLibreService.createGiftCard(dataFromStorage, function(err, giftCard) {
if (err) {
popupService.showAlert('Error creating gift card', err);
return;
}
if (giftCard.status != 'PENDING') {
var newData = {};
if (!giftCard.status) dataFromStorage.status = null; // Fix error from server
var cardStatus = giftCard.cardStatus;
if (cardStatus && (cardStatus != 'active' && cardStatus != 'inactive' && cardStatus != 'expired'))
giftCard.status = 'FAILURE';
lodash.merge(newData, dataFromStorage, giftCard);
mercadoLibreService.savePendingGiftCard(newData, null, function(err) {
$log.debug("Saving new gift card");
updateGiftCards();
});
}
});
}
});
});
}, 1000, {
'leading': true
});
$scope.openCardModal = function(card) {
$scope.card = card;
$ionicModal.fromTemplateUrl('views/modals/mercadolibre-card-details.html', {
scope: $scope
}).then(function(modal) {
$scope.mercadoLibreCardDetailsModal = modal;
$scope.mercadoLibreCardDetailsModal.show();
});
$scope.$on('modal.hidden', function() {
$scope.updatePendingGiftCards();
});
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.invoiceId = data.stateParams.invoiceId;
updateGiftCards(function() {
if ($scope.invoiceId) {
var card = lodash.find($scope.giftCards, {
invoiceId: $scope.invoiceId
});
if (lodash.isEmpty(card)) {
popupService.showAlert(null, 'Card not found');
return;
}
$scope.openCardModal(card);
}
});
});
$scope.$on("$ionicView.afterEnter", function(event, data) {
$scope.updatePendingGiftCards();
});
});

View file

@ -18,14 +18,14 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'superEconomy'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
var getMaxRecommended = function() {
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'urgent'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
$scope.ok = function() {
@ -61,7 +61,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
// If no custom fee
if (value) {
$scope.customFeePerKB = null;
$scope.feePerSatByte = (value.feePerKB / 1000).toFixed();
$scope.feePerSatByte = (value.feePerKb / 1000).toFixed();
$scope.avgConfirmationTime = value.nbBlocks * 10;
} else {
$scope.avgConfirmationTime = null;
@ -102,7 +102,7 @@ angular.module('copayApp.controllers').controller('feeLevelsController', functio
$scope.feeOpts = feeService.feeOpts;
$scope.loadingFee = true;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.coin, function(err, levels) {
$scope.loadingFee = false;
if (err || lodash.isEmpty(levels)) {
showErrorAndClose(null, err);

View file

@ -0,0 +1,21 @@
'use strict';
angular.module('copayApp.controllers').controller('mercadoLibreCardDetailsController', function($scope, mercadoLibreService, externalLinkService) {
$scope.remove = function() {
mercadoLibreService.savePendingGiftCard($scope.card, {
remove: true
}, function(err) {
$scope.close();
});
};
$scope.close = function() {
$scope.mercadoLibreCardDetailsModal.hide();
};
$scope.openExternalLink = function(url) {
externalLinkService.open(url);
};
});

View file

@ -23,7 +23,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
function displayFeeValues() {
txFormatService.formatAlternativeStr($scope.tx.fee, function(v) {
txFormatService.formatAlternativeStr($scope.wallet.coin, $scope.tx.fee, function(v) {
$scope.tx.feeFiatStr = v;
});
$scope.tx.feeRateStr = ($scope.tx.fee / ($scope.tx.amount + $scope.tx.fee) * 100).toFixed(2) + '%';
@ -31,17 +31,23 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
};
function applyButtonText() {
$scope.buttonText = $scope.isCordova && !$scope.isWindowsPhoneApp ? gettextCatalog.getString('Slide') + ' ' : gettextCatalog.getString('Click') + ' ';
var lastSigner = lodash.filter($scope.tx.actions, {
type: 'accept'
}).length == $scope.tx.requiredSignatures - 1;
if (lastSigner) {
$scope.buttonText += gettextCatalog.getString('to send');
if ($scope.isCordova && !$scope.isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to send');
} else {
$scope.buttonText = gettextCatalog.getString('Click to send');
}
$scope.successText = gettextCatalog.getString('Payment Sent');
} else {
$scope.buttonText += gettextCatalog.getString('to accept');
if ($scope.isCordova && !$scope.isWindowsPhoneApp) {
$scope.buttonText = gettextCatalog.getString('Slide to accept');
} else {
$scope.buttonText = gettextCatalog.getString('Click to accept');
}
$scope.successText = gettextCatalog.getString('Payment Accepted');
}
};
@ -219,7 +225,7 @@ angular.module('copayApp.controllers').controller('txpDetailsController', functi
copayerId: $scope.wallet.credentials.copayerId
});
$scope.tx = txFormatService.processTx(tx);
$scope.tx = txFormatService.processTx($scope.wallet.coin, tx);
if (!action && tx.status == 'pending')
$scope.tx.pendingForUs = true;

View file

@ -27,7 +27,7 @@ angular.module('copayApp.controllers').controller('tourController',
rateService.whenAvailable(function() {
var localCurrency = 'USD';
var btcAmount = 1;
var rate = rateService.toFiat(btcAmount * 1e8, localCurrency);
var rate = rateService.toFiat(btcAmount * 1e8, localCurrency, 'btc');
$scope.localCurrencySymbol = '$';
$scope.localCurrencyPerBtc = $filter('formatFiatAmount')(parseFloat(rate.toFixed(2), 10));
$timeout(function() {

View file

@ -45,8 +45,7 @@ angular.module('copayApp.controllers').controller('paperWalletController',
$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.balance = txFormatService.formatAmountStr($scope.wallet.coin, balance);
}
$scope.$apply();
});
@ -60,9 +59,9 @@ angular.module('copayApp.controllers').controller('paperWalletController',
$scope.wallet.buildTxFromPrivateKey($scope.privateKey, destinationAddress, null, function(err, testTx) {
if (err) return cb(err);
var rawTxLength = testTx.serialize().length;
feeService.getCurrentFeeRate('livenet', function(err, feePerKB) {
feeService.getCurrentFeeRate('btc', 'livenet', function(err, feePerKb) {
var opts = {};
opts.fee = Math.round((feePerKB * rawTxLength) / 2000);
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({

View file

@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesController',
function($scope, $rootScope, $timeout, $log, $ionicHistory, configService, profileService, fingerprintService, walletService, platformInfo) {
function($scope, $rootScope, $timeout, $log, $ionicHistory, configService, profileService, fingerprintService, walletService, platformInfo, externalLinkService, gettextCatalog) {
var wallet;
var walletId;
@ -58,6 +58,16 @@ angular.module('copayApp.controllers').controller('preferencesController',
}
};
$scope.openWikiSpendingPassword = function() {
var url = 'https://github.com/bitpay/copay/wiki/COPAY---FAQ#what-the-spending-password-does';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Read more in our Wiki');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.touchIdChange = function() {
var newStatus = $scope.touchIdEnabled.value;
walletService.setTouchId(wallet, !!newStatus, function(err) {

View file

@ -43,8 +43,9 @@ angular.module('copayApp.controllers').controller('preferencesAltCurrencyControl
$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());
var val = item.name
var val2 = item.isoCode;
return lodash.includes(val.toLowerCase(), search.toLowerCase()) || lodash.includes(val2.toLowerCase(), search.toLowerCase());
});
$timeout(function() {
$scope.$apply();

View file

@ -0,0 +1,42 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesCashController', function($scope, $log, $timeout, appConfigService, configService, gettextCatalog, externalLinkService) {
var updateConfig = function() {
var config = configService.getSync();
$scope.appName = appConfigService.nameCase;
$scope.cashSupport = {
value: config.cashSupport
};
$timeout(function() {
$scope.$apply();
});
};
$scope.cashSupportChange = function() {
var opts = {
cashSupport: $scope.cashSupport.value
};
configService.set(opts, function(err) {
if (err) $log.debug(err);
});
};
$scope.openBitcoinCashWeb = function() {
var url = 'https://www.bitcoincash.org/';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Open bitcoincash.org?');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
$scope.$on("$ionicView.beforeEnter", function(event, data) {
updateConfig();
});
});

View file

@ -2,7 +2,7 @@
angular.module('copayApp.controllers').controller('preferencesDeleteWalletController',
function($scope, $ionicHistory, gettextCatalog, lodash, profileService, $state, ongoingProcess, popupService, pushNotificationsService) {
$scope.$on("$ionicView.beforeEnter", function(event, data) {
if (!data.stateParams || !data.stateParams.walletId) {
popupService.showAlert(null, gettextCatalog.getString('No wallet selected'), function() {
@ -17,8 +17,7 @@ angular.module('copayApp.controllers').controller('preferencesDeleteWalletContro
});
return;
}
$scope.alias = lodash.isEqual($scope.wallet.name, $scope.wallet.credentials.walletName) ? null : $scope.wallet.name + ' ';
$scope.walletName = $scope.wallet.credentials.walletName;
$scope.walletName = $scope.wallet.name;
});
$scope.showDeletePopup = function() {

View file

@ -32,11 +32,12 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
});
$scope.init = function() {
var coin = 'btc'; // TODO: only BTC in preferences
$scope.network = $scope.network || 'livenet';
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = $scope.feeLevel || feeService.getCurrentFeeLevel();
$scope.loadingFee = true;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels(coin, function(err, levels) {
$scope.loadingFee = false;
if (err) {
//Error is already formatted
@ -66,7 +67,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
return;
}
$scope.feePerSatByte = (value.feePerKB / 1000).toFixed();
$scope.feePerSatByte = (value.feePerKb / 1000).toFixed();
$scope.avgConfirmationTime = value.nbBlocks * 10;
$scope.invalidCustomFeeEntered = false;
setMinWarning();
@ -97,7 +98,7 @@ angular.module('copayApp.controllers').controller('preferencesFeeController', fu
var value = lodash.find($scope.feeLevels[$scope.network], {
level: 'superEconomy'
});
return parseInt((value.feePerKB / 1000).toFixed());
return parseInt((value.feePerKb / 1000).toFixed());
};
var setMinWarning = function() {

View file

@ -1,43 +0,0 @@
'use strict';
angular.module('copayApp.controllers').controller('preferencesUnitController', function($scope, $log, configService, $ionicHistory, gettextCatalog, walletService, profileService) {
var config = configService.getSync();
$scope.unitList = [{
name: 'bits (1,000,000 bits = 1BTC)',
shortName: 'bits',
value: 100,
decimals: 2,
code: 'bit',
}, {
name: 'BTC',
shortName: 'BTC',
value: 100000000,
decimals: 8,
code: 'btc',
}];
$scope.save = function(newUnit) {
var opts = {
wallet: {
settings: {
unitName: newUnit.shortName,
unitToSatoshi: newUnit.value,
unitDecimals: newUnit.decimals,
unitCode: newUnit.code,
}
}
};
configService.set(opts, function(err) {
if (err) $log.warn(err);
$ionicHistory.goBack();
walletService.updateRemotePreferences(profileService.getWallets())
});
};
$scope.$on("$ionicView.enter", function(event, data){
$scope.currentUnit = config.wallet.settings.unitCode;
});
});

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('sellCoinbaseController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicScrollDelegate, $ionicConfig, lodash, coinbaseService, popupService, profileService, ongoingProcess, walletService, appConfigService, configService, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -34,124 +35,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
}, onSendStatusChange);
};
var checkTransaction = lodash.throttle(function(count, txp) {
$log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5');
// TX amount in BTC
var satToBtc = 1 / 100000000;
var amountBTC = (txp.amount * satToBtc).toFixed(8);
coinbaseService.init(function(err, res) {
if (err) {
$log.error(err);
checkTransaction(count, txp);
return;
}
var accessToken = res.accessToken;
var accountId = res.accountId;
var sellPrice = null;
coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) {
if (err) {
$log.debug(err);
checkTransaction(count, txp);
return;
}
sellPrice = sell.data;
coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) {
if (err) {
$log.debug(err);
checkTransaction(count, txp);
return;
}
var coinbaseTransactions = ctxs.data;
var txFound = false;
var ctx;
for(var i = 0; i < coinbaseTransactions.length; i++) {
ctx = coinbaseTransactions[i];
if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) {
$log.warn('Transaction found!', ctx);
txFound = true;
$log.debug('Saving transaction to process later...');
ctx['payment_method'] = $scope.selectedPaymentMethodId.value;
ctx['status'] = 'pending'; // Forcing "pending" status to process later
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data;
ctx['sell_price_amount'] = sellPrice ? sellPrice.amount : '';
ctx['sell_price_currency'] = sellPrice ? sellPrice.currency : 'USD';
ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name;
coinbaseService.savePendingTransaction(ctx, null, function(err) {
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
if (err) $log.debug(err);
});
return;
}
}
if (!txFound) {
// Transaction sent, but could not be verified by Coinbase.com
$log.warn('Transaction not found in Coinbase.');
if (count < 5) {
checkTransaction(count + 1, txp);
} else {
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
showError('No transaction found');
return;
}
}
});
});
});
}, 8000, {
'leading': true
});
var statusChangeHandler = function (processName, showName, isOn) {
$log.debug('statusChangeHandler: ', processName, showName, isOn);
if ( processName == 'sellingBitcoin' && !isOn) {
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
}, 100);
} else if (showName) {
$scope.sendStatus = showName;
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.priceSensitivity = coinbaseService.priceSensitivity;
$scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity };
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
minAmount: parsedAmount.amountSat
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingCoinbase', true);
coinbaseService.init(function(err, res) {
if (err) {
@ -193,7 +77,121 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
if (!hasPrimary) $scope.selectedPaymentMethodId.value = $scope.paymentMethods[0].id;
$scope.sellRequest();
});
});
});
};
var checkTransaction = lodash.throttle(function(count, txp) {
$log.warn('Check if transaction has been received by Coinbase. Try ' + count + '/5');
// TX amount in BTC
var satToBtc = 1 / 100000000;
var amountBTC = (txp.amount * satToBtc).toFixed(8);
coinbaseService.init(function(err, res) {
if (err) {
$log.error(err);
checkTransaction(count, txp);
return;
}
var accessToken = res.accessToken;
var accountId = res.accountId;
var sellPrice = null;
coinbaseService.sellPrice(accessToken, coinbaseService.getAvailableCurrency(), function(err, sell) {
if (err) {
$log.debug(err);
checkTransaction(count, txp);
return;
}
sellPrice = sell.data;
coinbaseService.getTransactions(accessToken, accountId, function(err, ctxs) {
if (err) {
$log.debug(err);
checkTransaction(count, txp);
return;
}
var coinbaseTransactions = ctxs.data;
var txFound = false;
var ctx;
for(var i = 0; i < coinbaseTransactions.length; i++) {
ctx = coinbaseTransactions[i];
if (ctx.type == 'send' && ctx.from && ctx.amount.amount == amountBTC ) {
$log.warn('Transaction found!', ctx);
txFound = true;
$log.debug('Saving transaction to process later...');
ctx['payment_method'] = $scope.selectedPaymentMethodId.value;
ctx['status'] = 'pending'; // Forcing "pending" status to process later
ctx['price_sensitivity'] = $scope.selectedPriceSensitivity.data;
ctx['sell_price_amount'] = sellPrice ? sellPrice.amount : '';
ctx['sell_price_currency'] = sellPrice ? sellPrice.currency : 'USD';
ctx['description'] = appConfigService.nameCase + ' Wallet: ' + $scope.wallet.name;
coinbaseService.savePendingTransaction(ctx, null, function(err) {
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
if (err) $log.debug(err);
});
return;
}
}
if (!txFound) {
// Transaction sent, but could not be verified by Coinbase.com
$log.warn('Transaction not found in Coinbase.');
if (count < 5) {
checkTransaction(count + 1, txp);
} else {
ongoingProcess.set('sellingBitcoin', false, statusChangeHandler);
showError('No transaction found');
return;
}
}
});
});
});
}, 8000, {
'leading': true
});
var statusChangeHandler = function (processName, showName, isOn) {
$log.debug('statusChangeHandler: ', processName, showName, isOn);
if ( processName == 'sellingBitcoin' && !isOn) {
$scope.sendStatus = 'success';
$timeout(function() {
$scope.$digest();
}, 100);
} else if (showName) {
$scope.sendStatus = showName;
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.priceSensitivity = coinbaseService.priceSensitivity;
$scope.selectedPriceSensitivity = { data: coinbaseService.selectedPriceSensitivity };
$scope.network = coinbaseService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
$scope.sellRequest = function() {
@ -236,7 +234,7 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
var cancelText = 'Cancel';
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
if (!ok) return;
ongoingProcess.set('sellingBitcoin', true, statusChangeHandler);
coinbaseService.init(function(err, res) {
if (err) {
@ -294,8 +292,8 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
checkTransaction(1, txSent);
});
});
});
});
});
});
});
};
@ -306,6 +304,15 @@ angular.module('copayApp.controllers').controller('sellCoinbaseController', func
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -2,6 +2,7 @@
angular.module('copayApp.controllers').controller('sellGlideraController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, glideraService, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, txFormatService) {
var coin = 'btc';
var amount;
var currency;
@ -35,39 +36,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
}
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'bits' && data.stateParams.currency != 'BTC' ? true : false;
var parsedAmount = txFormatService.parseAmount(
data.stateParams.amount,
data.stateParams.currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
minAmount: parsedAmount.amountSat
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.wallet = $scope.wallets[0]; // Default first wallet
var processPaymentInfo = function() {
ongoingProcess.set('connectingGlidera', true);
glideraService.init(function(err, data) {
if (err) {
@ -91,6 +60,35 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
$scope.sellInfo = sell;
});
});
};
$scope.$on("$ionicView.beforeLeave", function(event, data) {
$ionicConfig.views.swipeBackEnabled(true);
});
$scope.$on("$ionicView.enter", function(event, data) {
$ionicConfig.views.swipeBackEnabled(false);
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
$scope.isFiat = data.stateParams.currency != 'BTC' ? true : false;
amount = data.stateParams.amount;
currency = data.stateParams.currency;
$scope.network = glideraService.getNetwork();
$scope.wallets = profileService.getWallets({
m: 1, // Only 1-signature wallet
onlyComplete: true,
network: $scope.network,
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
showErrorAndBack('Insufficient funds');
return;
}
$scope.onWalletSelect($scope.wallets[0]); // Default first wallet
});
var ask2FaCode = function(mode, cb) {
@ -108,7 +106,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
popupService.showPrompt(title, message, null, function(twoFaCode) {
if (typeof twoFaCode == 'undefined') return cb();
return cb(twoFaCode);
});
});
} else {
return cb();
}
@ -119,7 +117,7 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
var okText = 'Confirm';
var cancelText = 'Cancel';
popupService.showConfirm(null, message, okText, cancelText, function(ok) {
if (!ok) return;
if (!ok) return;
ongoingProcess.set('sellingBitcoin', true, statusChangeHandler);
glideraService.get2faCode($scope.token, function(err, tfa) {
if (err) {
@ -231,6 +229,15 @@ angular.module('copayApp.controllers').controller('sellGlideraController', funct
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
var parsedAmount = txFormatService.parseAmount(
coin,
amount,
currency);
amount = parsedAmount.amount;
currency = parsedAmount.currency;
$scope.amountUnitStr = parsedAmount.amountUnitStr;
processPaymentInfo();
};
$scope.goBackHome = function() {

View file

@ -206,14 +206,24 @@ angular.module('copayApp.controllers').controller('tabHomeController',
};
var updateAllWallets = function() {
$scope.wallets = profileService.getWallets();
if (lodash.isEmpty($scope.wallets)) return;
var wallets = [];
$scope.walletsBtc = profileService.getWallets({coin: 'btc'});
$scope.walletsBch = profileService.getWallets({coin: 'bch'});
var i = $scope.wallets.length;
lodash.each($scope.walletsBtc, function(wBtc) {
wallets.push(wBtc);
});
lodash.each($scope.walletsBch, function(wBch) {
wallets.push(wBch);
});
if (lodash.isEmpty(wallets)) return;
var i = wallets.length;
var j = 0;
var timeSpan = 60 * 60 * 24 * 7;
lodash.each($scope.wallets, function(wallet) {
lodash.each(wallets, function(wallet) {
walletService.getStatus(wallet, {}, function(err, status) {
if (err) {

View file

@ -8,7 +8,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.requestSpecificAmount = function() {
$state.go('tabs.paymentRequest.amount', {
id: $scope.wallet.credentials.walletId
id: $scope.wallet.credentials.walletId,
coin: $scope.wallet.coin
});
};
@ -123,8 +124,13 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
return wallet;
}
var setProtocolHandler = function() {
$scope.protocolHandler = walletService.getProtocolHandler($scope.wallet);
}
$scope.onWalletSelect = function(wallet) {
$scope.wallet = wallet;
setProtocolHandler();
$scope.setAddress();
};
@ -136,6 +142,8 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
$scope.shareAddress = function() {
if (!$scope.isCordova) return;
window.plugins.socialsharing.share('bitcoin:' + $scope.addr, null, null, null);
var protocol = 'bitcoin';
if ($scope.wallet.coin == 'bch') protocol += 'cash';
window.plugins.socialsharing.share(protocol + ':' + $scope.addr, null, null, null);
}
});

View file

@ -1,6 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService) {
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService, bitcoreCash) {
var originalList;
var CONTACTS_SHOW_LIMIT;
@ -76,6 +76,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
color: v.color,
name: v.name,
recipientType: 'wallet',
coin: v.coin,
network: v.network,
getAddress: function(cb) {
walletService.getAddress(v, false, cb);
},
@ -85,6 +87,14 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
}
}
var getCoin = function(address) {
var cashAddress = bitcoreCash.Address.isValid(address, 'livenet');
if (cashAddress) {
return 'bch';
}
return 'btc';
};
var updateContactsList = function(cb) {
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
@ -99,6 +109,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
address: k,
email: lodash.isObject(v) ? v.email : null,
recipientType: 'contact',
coin: getCoin(k),
getAddress: function(cb) {
return cb(null, k);
},
@ -186,7 +197,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
toAddress: addr,
toName: item.name,
toEmail: item.email,
toColor: item.color
toColor: item.color,
coin: item.coin
})
});
});

View file

@ -6,11 +6,11 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
$scope.currentLanguageName = uxLanguage.getCurrentLanguageName();
$scope.feeOpts = feeService.feeOpts;
$scope.currentFeeLevel = feeService.getCurrentFeeLevel();
$scope.wallets = profileService.getWallets();
$scope.walletsBtc = profileService.getWallets({ coin: 'btc' });
$scope.walletsBch = profileService.getWallets({ coin: 'bch' });
$scope.buyAndSellServices = buyAndSellService.getLinked();
configService.whenAvailable(function(config) {
$scope.unitName = config.wallet.settings.unitName;
$scope.selectedAlternative = {
name: config.wallet.settings.alternativeName,
isoCode: config.wallet.settings.alternativeIsoCode
@ -26,6 +26,11 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
}, 10);
});
$scope.cashSupport = {
value: config.cashSupport
};
// TODO move this to a generic service
bitpayCardService.getCards(function(err, cards) {
if (err) $log.error(err);
@ -63,6 +68,8 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
});
});
$scope.$on("$ionicView.enter", function(event, data) {
updateConfig();
});

View file

@ -3,6 +3,7 @@
angular.module('copayApp.controllers').controller('topUpController', function($scope, $log, $state, $timeout, $ionicHistory, $ionicConfig, lodash, popupService, profileService, ongoingProcess, walletService, configService, platformInfo, bitpayService, bitpayCardService, payproService, bwcError, txFormatService, sendMaxService, gettextCatalog) {
$scope.isCordova = platformInfo.isCordova;
var coin = 'btc';
var cardId;
var useSendMax;
var amount;
@ -36,7 +37,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
};
var satToFiat = function(sat, cb) {
txFormatService.toFiat(sat, $scope.currencyIsoCode, function(value) {
txFormatService.toFiat(coin, sat, $scope.currencyIsoCode, function(value) {
return cb(value);
});
};
@ -218,7 +219,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
// Save TX in memory
createdTx = ctxp;
$scope.totalAmountStr = txFormatService.formatAmountStr(ctxp.amount);
$scope.totalAmountStr = txFormatService.formatAmountStr(coin, ctxp.amount);
setTotalAmount(parsedAmount.amountSat, invoiceFeeSat, ctxp.fee);
@ -256,7 +257,8 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
$scope.wallets = profileService.getWallets({
onlyComplete: true,
network: bitpayService.getEnvironment().network,
hasFunds: true
hasFunds: true,
coin: coin
});
if (lodash.isEmpty($scope.wallets)) {
@ -319,7 +321,7 @@ angular.module('copayApp.controllers').controller('topUpController', function($s
});
return;
}
var parsedAmount = txFormatService.parseAmount(a, c);
var parsedAmount = txFormatService.parseAmount(coin, a, c);
initializeTopUp(wallet, parsedAmount);
});
};

View file

@ -5,6 +5,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
var txId;
var listeners = [];
var config = configService.getSync();
var blockexplorerUrl;
$scope.$on("$ionicView.beforeEnter", function(event, data) {
txId = data.stateParams.txid;
@ -15,6 +16,12 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.isShared = $scope.wallet.credentials.n > 1;
$scope.txsUnsubscribedForNotifications = config.confirmedTxsNotifications ? !config.confirmedTxsNotifications.enabled : true;
if ($scope.wallet.coin == 'bch') {
blockexplorerUrl = 'bch-insight.bitpay.com';
} else {
blockexplorerUrl = 'insight.bitpay.com';
}
txConfirmNotification.checkIfEnabled(txId, function(res) {
$scope.txNotification = {
value: res
@ -40,6 +47,16 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
});
});
$scope.readMore = function() {
var url = 'https://github.com/bitpay/copay/wiki/COPAY---FAQ#amount-too-low-to-spend';
var optIn = true;
var title = null;
var message = gettextCatalog.getString('Read more in our Wiki');
var okText = gettextCatalog.getString('Open');
var cancelText = gettextCatalog.getString('Go Back');
externalLinkService.open(url, optIn, title, message, okText, cancelText);
};
function updateMemo() {
walletService.getTxNote($scope.wallet, $scope.btx.txid, function(err, note) {
if (err) {
@ -102,8 +119,8 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
return popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Transaction not available at this time'));
}
$scope.btx = txFormatService.processTx(tx);
txFormatService.formatAlternativeStr(tx.fees, function(v) {
$scope.btx = txFormatService.processTx($scope.wallet.coin, tx);
txFormatService.formatAlternativeStr($scope.wallet.coin, tx.fees, function(v) {
$scope.btx.feeFiatStr = v;
$scope.btx.feeRateStr = ($scope.btx.fees / ($scope.btx.amount + $scope.btx.fees) * 100).toFixed(2) + '%';
});
@ -121,7 +138,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.$digest();
});
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
if (err) return;
walletService.getLowAmount($scope.wallet, levels, function(err, amount) {
if (err) return;
@ -168,10 +185,7 @@ angular.module('copayApp.controllers').controller('txDetailsController', functio
$scope.viewOnBlockchain = function() {
var btx = $scope.btx;
var url = 'https://blockchain.info/tx/' + btx.txid;
if ($scope.getShortNetworkName() == 'test') {
url = "https://test-insight.bitpay.com/tx/" + btx.txid;
}
var url = 'https://' + ($scope.getShortNetworkName() == 'test' ? 'test-' : '') + blockexplorerUrl + '/tx/' + btx.txid;
var optIn = true;
var title = null;
var message = gettextCatalog.getString('View Transaction on Insight');

View file

@ -52,7 +52,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var analyzeUtxos = function() {
if (analyzeUtxosDone) return;
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
if (err) return;
walletService.getLowUtxos($scope.wallet, levels, function(err, resp) {
if (err || !resp) return;
@ -156,9 +156,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
var updateTxHistory = function(cb) {
if (!cb) cb = function() {};
if ($scope.updatingTxHistory) return;
$scope.updatingTxHistory = true;
$scope.updateTxHistoryError = false;
$scope.updatingTxHistoryProgress = 0;
@ -171,7 +169,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
});
};
feeService.getFeeLevels(function(err, levels) {
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
walletService.getTxHistory($scope.wallet, {
progressFn: progressFn,
feeLevels: levels,
@ -184,7 +182,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
}
$scope.completeTxHistory = txHistory;
$scope.showHistory();
$scope.$apply();
$timeout(function() {
$scope.$apply();
});
return cb();
});
});
@ -356,6 +356,8 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
if (!$scope.wallet) return;
$scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;
$scope.updatingTxHistory = true;
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};