Merge branch 'wallet/sprint/20' into wallet/dev
This commit is contained in:
commit
c8d7e88f81
105 changed files with 5417 additions and 1644 deletions
|
|
@ -21,6 +21,9 @@ angular.module('copayApp.controllers').controller('addressbookAddController', fu
|
|||
$timeout(function() {
|
||||
var form = addressbookForm;
|
||||
if (data && form) {
|
||||
if (data.result) {
|
||||
data = data.result;
|
||||
}
|
||||
data = data.replace(/^bitcoin(cash)?:/, '');
|
||||
form.address.$setViewValue(data);
|
||||
form.address.$isValid = true;
|
||||
|
|
@ -36,9 +39,9 @@ angular.module('copayApp.controllers').controller('addressbookAddController', fu
|
|||
addressbook.address = translated.legacy;
|
||||
}
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("contact_created", [{
|
||||
"coin": $scope.addressbookEntry.coin
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, $state, $timeout, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog, configService, bitcoinCashJsService) {
|
||||
angular.module('copayApp.controllers').controller('addressbookViewController', function($scope, sendFlowService, $state, $timeout, lodash, addressbookService, popupService, $ionicHistory, platformInfo, gettextCatalog, configService, bitcoinCashJsService) {
|
||||
|
||||
var config = configService.getSync();
|
||||
var defaults = configService.getDefaults();
|
||||
|
|
@ -22,6 +22,7 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
|
|||
|
||||
$scope.sendTo = function() {
|
||||
$ionicHistory.removeBackView();
|
||||
sendFlowService.clear();
|
||||
$state.go('tabs.send');
|
||||
$timeout(function() {
|
||||
var to = '';
|
||||
|
|
@ -31,12 +32,16 @@ angular.module('copayApp.controllers').controller('addressbookViewController', f
|
|||
} else {
|
||||
to = $scope.addressbookEntry.address;
|
||||
}
|
||||
$state.transitionTo('tabs.send.amount', {
|
||||
|
||||
var stateParams = {
|
||||
toAddress: to,
|
||||
toName: $scope.addressbookEntry.name,
|
||||
toEmail: $scope.addressbookEntry.email,
|
||||
coin: $scope.addressbookEntry.coin
|
||||
});
|
||||
};
|
||||
|
||||
sendFlowService.pushState(stateParams);
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,68 +1,167 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('amountController', function($scope, $filter, $timeout, $ionicModal, $ionicScrollDelegate, $ionicHistory, storageService, walletService, gettextCatalog, platformInfo, lodash, configService, rateService, $stateParams, $window, $state, $log, txFormatService, ongoingProcess, popupService, bwcError, payproService, profileService, bitcore, amazonService, nodeWebkitService) {
|
||||
angular.module('copayApp.controllers').controller('amountController', amountController);
|
||||
|
||||
function amountController(configService, $filter, gettextCatalog, $ionicHistory, $ionicModal, $ionicScrollDelegate, lodash, $log, nodeWebkitService, rateService, $scope, $state, $timeout, sendFlowService, shapeshiftService, txFormatService, platformInfo, profileService, walletService, $window) {
|
||||
var vm = this;
|
||||
|
||||
vm.allowSend = false;
|
||||
vm.altCurrencyList = [];
|
||||
vm.alternativeAmount = '';
|
||||
vm.alternativeUnit = '';
|
||||
vm.amount = '0';
|
||||
vm.availableFunds = '';
|
||||
// Use insufficient for logic, as when the amount is invalid, funds being
|
||||
// either sufficent or insufficient doesn't make sense.
|
||||
vm.fundsAreInsufficient = false;
|
||||
vm.globalResult = '';
|
||||
vm.isRequestingSpecificAmount = false;
|
||||
vm.listComplete = false;
|
||||
vm.lastUsedPopularList = [];
|
||||
vm.maxAmount = 0;
|
||||
vm.minAmount = 0;
|
||||
vm.thirdParty = false;
|
||||
vm.unit = '';
|
||||
|
||||
vm.changeUnit = changeUnit;
|
||||
vm.close = close;
|
||||
vm.findCurrency = findCurrency;
|
||||
vm.finish = finish;
|
||||
vm.goBack = goBack;
|
||||
vm.loadMore = loadMore;
|
||||
vm.openPopup = openPopup;
|
||||
vm.pushDigit = pushDigit;
|
||||
vm.removeDigit = removeDigit;
|
||||
vm.save = save;
|
||||
vm.sendMax = sendMax;
|
||||
vm.errorMessage = '';
|
||||
|
||||
$scope.$on('$ionicView.beforeEnter', onBeforeEnter);
|
||||
$scope.$on('$ionicView.leave', onLeave);
|
||||
|
||||
var _id;
|
||||
var unitToSatoshi;
|
||||
var satToUnit;
|
||||
var unitDecimals;
|
||||
var satToBtc;
|
||||
var SMALL_FONT_SIZE_LIMIT = 10;
|
||||
var LENGTH_EXPRESSION_LIMIT = 19;
|
||||
var LENGTH_BEFORE_COMMA_EXPRESSION_LIMIT = 8;
|
||||
var LENGTH_AFTER_COMMA_EXPRESSION_LIMIT = 8;
|
||||
var isNW = platformInfo.isNW;
|
||||
|
||||
var unitIndex = 0;
|
||||
var altCurrencyModal = null;
|
||||
var altUnitIndex = 0;
|
||||
var availableFundsInCrypto = '';
|
||||
var availableFundsInFiat = '';
|
||||
var availableSatoshis = null;
|
||||
var availableUnits = [];
|
||||
var fiatCode;
|
||||
var isNW = platformInfo.isNW;
|
||||
var isAndroid = platformInfo.isAndroid;
|
||||
var isIos = platformInfo.isIOS;
|
||||
var lastUsedAltCurrencyList = [];
|
||||
var passthroughParams = {};
|
||||
var satToUnit;
|
||||
var unitDecimals;
|
||||
var unitIndex = 0;
|
||||
var unitToSatoshi;
|
||||
var useSendMax = false;
|
||||
|
||||
var fixedUnit;
|
||||
|
||||
$scope.amountModel = { amount: 0 };
|
||||
|
||||
$scope.isChromeApp = platformInfo.isChromeApp;
|
||||
$scope.isAndroid = platformInfo.isAndroid;
|
||||
$scope.isIos = platformInfo.isIOS;
|
||||
|
||||
$scope.$on('$ionicView.leave', function() {
|
||||
function onLeave() {
|
||||
angular.element($window).off('keydown');
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
function onBeforeEnter(event, data) {
|
||||
console.log('amount onBeforeEnter sendflow ', sendFlowService.getState());
|
||||
|
||||
if (data.direction == "back") {
|
||||
sendFlowService.popState();
|
||||
}
|
||||
|
||||
initCurrencies();
|
||||
|
||||
if (data.stateParams.shapeshiftOrderId && data.stateParams.shapeshiftOrderId.length > 0) {
|
||||
$scope.minShapeshiftAmount = parseFloat(data.stateParams.minShapeshiftAmount);
|
||||
$scope.maxShapeshiftAmount = parseFloat(data.stateParams.maxShapeshiftAmount);
|
||||
$scope.shapeshiftOrderId = data.stateParams.shapeshiftOrderId;
|
||||
}
|
||||
passthroughParams = sendFlowService;
|
||||
|
||||
// To get the wallet from with the new flow
|
||||
$scope.fromWalletId = data.stateParams.fromWalletId;
|
||||
vm.fromWalletId = passthroughParams.fromWalletId;
|
||||
vm.toWalletId = passthroughParams.toWalletId;
|
||||
vm.minAmount = parseFloat(passthroughParams.minAmount);
|
||||
vm.maxAmount = parseFloat(passthroughParams.maxAmount);
|
||||
|
||||
if (data.stateParams.noPrefix) {
|
||||
$scope.showWarningMessage = data.stateParams.noPrefix != 0;
|
||||
if ($scope.showWarningMessage) {
|
||||
var message = 'Address doesn\'t contain currency information, please make sure you are sending the correct currency.';
|
||||
popupService.showAlert('', message, function() {}, 'Ok');
|
||||
if (passthroughParams.thirdParty) {
|
||||
vm.thirdParty = passthroughParams.thirdParty; // Parse stringified JSON-object
|
||||
if (vm.thirdParty) {
|
||||
if (vm.thirdParty.id === 'shapeshift') {
|
||||
if (!vm.thirdParty.data) {
|
||||
vm.thirdParty.data = {};
|
||||
}
|
||||
vm.thirdParty.data['fromWalletId'] = vm.fromWalletId;
|
||||
|
||||
vm.fromWallet = profileService.getWallet(vm.fromWalletId);
|
||||
vm.toWallet = profileService.getWallet(vm.toWalletId);
|
||||
|
||||
shapeshiftService.getMarketData(vm.fromWallet.coin, vm.toWallet.coin, function(data) {
|
||||
vm.thirdParty.data['minAmount'] = vm.minAmount = parseFloat(data.minimum);
|
||||
vm.thirdParty.data['maxAmount'] = vm.maxAmount = parseFloat(data.maxLimit);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vm.isRequestingSpecificAmount = !passthroughParams.fromWalletId;
|
||||
|
||||
var config = configService.getSync().wallet.settings;
|
||||
|
||||
setAvailableUnits();
|
||||
updateUnitUI();
|
||||
|
||||
var reNr = /^[1234567890\.]$/;
|
||||
var reOp = /^[\*\+\-\/]$/;
|
||||
|
||||
if (!isAndroid && !isIos) {
|
||||
var disableKeys = angular.element($window).on('keydown', function(e) {
|
||||
if (!e.key) return;
|
||||
if (e.which === 8) { // you can add others here inside brackets.
|
||||
if (!altCurrencyModal) {
|
||||
e.preventDefault();
|
||||
vm.removeDigit();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key.match(reNr)) {
|
||||
vm.pushDigit(e.key);
|
||||
} else if (e.key.match(reOp)) {
|
||||
pushOperator(e.key);
|
||||
} else if (e.keyCode === 86) {
|
||||
if (e.ctrlKey || e.metaKey) processClipboard();
|
||||
} else if (e.keyCode === 13) vm.finish();
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
unitToSatoshi = config.unitToSatoshi;
|
||||
satToUnit = 1 / unitToSatoshi;
|
||||
unitDecimals = config.unitDecimals;
|
||||
|
||||
resetAmount();
|
||||
|
||||
processAmount();
|
||||
|
||||
$timeout(function() {
|
||||
$ionicScrollDelegate.resize();
|
||||
}, 10);
|
||||
|
||||
function setAvailableUnits() {
|
||||
var defaults = configService.getDefaults();
|
||||
var configCache = configService.getSync();
|
||||
availableUnits = [];
|
||||
|
||||
var hasBCHWallets = profileService.getWallets({
|
||||
coin: 'bch'
|
||||
}).length;
|
||||
var coinFromWallet = '';
|
||||
if (passthroughParams.fromWalletId) {
|
||||
var fromWallet = profileService.getWallet(passthroughParams.fromWalletId);
|
||||
coinFromWallet = fromWallet.coin;
|
||||
} else {
|
||||
var toWallet = profileService.getWallet(passthroughParams.toWalletId);
|
||||
coinFromWallet = toWallet.coin;
|
||||
}
|
||||
|
||||
if (hasBCHWallets) {
|
||||
if (coinFromWallet === 'bch') {
|
||||
availableUnits.push({
|
||||
name: 'Bitcoin Cash',
|
||||
id: 'bch',
|
||||
|
|
@ -70,11 +169,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
});
|
||||
};
|
||||
|
||||
var hasBTCWallets = profileService.getWallets({
|
||||
coin: 'btc'
|
||||
}).length;
|
||||
|
||||
if (hasBTCWallets) {
|
||||
if (coinFromWallet === 'btc') {
|
||||
availableUnits.push({
|
||||
name: 'Bitcoin',
|
||||
id: 'btc',
|
||||
|
|
@ -84,31 +179,11 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
|
||||
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;
|
||||
if (passthroughParams.currency) {
|
||||
fiatCode = passthroughParams.currency;
|
||||
altUnitIndex = unitIndex
|
||||
unitIndex = availableUnits.length;
|
||||
} else {
|
||||
|
|
@ -125,142 +200,53 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
isFiat: true,
|
||||
});
|
||||
|
||||
if (data.stateParams.fixedUnit) {
|
||||
fixedUnit = true;
|
||||
}
|
||||
|
||||
unitIndex = lodash.findIndex(availableUnits, {
|
||||
isFiat: true
|
||||
});
|
||||
|
||||
altUnitIndex = 0;
|
||||
};
|
||||
|
||||
// Go to...
|
||||
_id = data.stateParams.id; // Optional (BitPay Card ID or Wallet ID)
|
||||
$scope.nextStep = data.stateParams.nextStep;
|
||||
|
||||
setAvailableUnits();
|
||||
updateUnitUI();
|
||||
|
||||
$scope.hasMaxAmount = true;
|
||||
if ($ionicHistory.backView().stateName == 'tabs.receive') {
|
||||
$scope.hasMaxAmount = false;
|
||||
}
|
||||
|
||||
$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.displayAddress = data.stateParams.displayAddress;
|
||||
$scope.toName = data.stateParams.toName;
|
||||
$scope.toEmail = data.stateParams.toEmail;
|
||||
$scope.toColor = data.stateParams.toColor;
|
||||
|
||||
if (!$scope.nextStep && !data.stateParams.toAddress) {
|
||||
$log.error('Bad params at amount')
|
||||
throw ('bad params');
|
||||
}
|
||||
|
||||
var reNr = /^[1234567890\.]$/;
|
||||
var reOp = /^[\*\+\-\/]$/;
|
||||
|
||||
if (!$scope.isAndroid && !$scope.isIos) {
|
||||
var disableKeys = angular.element($window).on('keydown', function(e) {
|
||||
if (!e.key) return;
|
||||
if (e.which === 8) { // you can add others here inside brackets.
|
||||
if (!$scope.altCurrencyModal) {
|
||||
e.preventDefault();
|
||||
$scope.removeDigit();
|
||||
}
|
||||
}
|
||||
|
||||
if (e.key.match(reNr)) {
|
||||
$scope.pushDigit(e.key);
|
||||
} 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();
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$scope.specificAmount = $scope.specificAlternativeAmount = '';
|
||||
$scope.isCordova = platformInfo.isCordova;
|
||||
unitToSatoshi = config.unitToSatoshi;
|
||||
satToUnit = 1 / unitToSatoshi;
|
||||
satToBtc = 1 / 100000000;
|
||||
unitDecimals = config.unitDecimals;
|
||||
|
||||
$scope.resetAmount();
|
||||
|
||||
// in SAT ALWAYS
|
||||
if ($stateParams.toAmount) {
|
||||
$scope.amountModel.amount = (($stateParams.toAmount) * satToUnit).toFixed(unitDecimals);
|
||||
}
|
||||
|
||||
$scope.processAmount();
|
||||
|
||||
$timeout(function() {
|
||||
$ionicScrollDelegate.resize();
|
||||
}, 10);
|
||||
});
|
||||
|
||||
$scope.goBack = function() {
|
||||
if ($scope.shapeshiftOrderId) {
|
||||
$state.go('tabs.send').then(function() {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.home').then(function() {
|
||||
$state.transitionTo('tabs.shapeshift');
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$ionicHistory.goBack();
|
||||
if (passthroughParams.fromWalletId) {
|
||||
var fromWallet = profileService.getWallet(passthroughParams.fromWalletId);
|
||||
updateAvailableFundsFromWallet(fromWallet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
$ionicHistory.goBack();
|
||||
}
|
||||
|
||||
function paste(value) {
|
||||
$scope.amountModel.amount = value;
|
||||
$scope.processAmount();
|
||||
vm.amount = value;
|
||||
processAmount();
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function processClipboard() {
|
||||
if (!isNW) return;
|
||||
var value = nodeWebkitService.readFromClipboard();
|
||||
if (value && evaluate(value) > 0) paste(evaluate(value));
|
||||
};
|
||||
}
|
||||
|
||||
$scope.sendMax = function() {
|
||||
$scope.useSendMax = true;
|
||||
$scope.finish();
|
||||
};
|
||||
|
||||
$scope.toggleAlternative = function() {
|
||||
if ($scope.amountModel.amount && isExpression($scope.amountModel.amount)) {
|
||||
var amount = evaluate(format($scope.amountModel.amount));
|
||||
$scope.globalResult = '= ' + processResult(amount);
|
||||
}
|
||||
};
|
||||
function sendMax() {
|
||||
useSendMax = true;
|
||||
finish();
|
||||
}
|
||||
|
||||
function updateUnitUI() {
|
||||
$scope.unit = availableUnits[unitIndex].shortName;
|
||||
$scope.alternativeUnit = availableUnits[altUnitIndex].shortName;
|
||||
vm.unit = availableUnits[unitIndex].shortName;
|
||||
vm.alternativeUnit = availableUnits[altUnitIndex].shortName;
|
||||
|
||||
$scope.processAmount();
|
||||
$log.debug('Update unit coin @amount unit:' + $scope.unit + " alternativeUnit:" + $scope.alternativeUnit);
|
||||
};
|
||||
processAmount();
|
||||
$log.debug('Update unit coin @amount unit:' + vm.unit + " alternativeUnit:" + vm.alternativeUnit);
|
||||
}
|
||||
|
||||
$scope.changeUnit = function() {
|
||||
function changeUnit() {
|
||||
|
||||
$scope.amountModel.amount = '0';
|
||||
|
||||
if (fixedUnit) return;
|
||||
vm.amount = '0';
|
||||
|
||||
if (!(availableUnits[unitIndex].isFiat && availableUnits.length > 2 && altUnitIndex == 0)) {
|
||||
unitIndex++;
|
||||
|
|
@ -275,153 +261,166 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
});
|
||||
}
|
||||
|
||||
updateAvailableFundsStringIfNeeded();
|
||||
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.amountModel.amount && $scope.amountModel.amount.length >= SMALL_FONT_SIZE_LIMIT) $scope.smallFont = true;
|
||||
else $scope.smallFont = false;
|
||||
};
|
||||
|
||||
$scope.pushDigit = function(digit) {
|
||||
if ($scope.amountModel.amount && digit != '.') {
|
||||
var amountSplitByComma = $scope.amountModel.amount.split('.');
|
||||
function pushDigit(digit) {
|
||||
if (vm.amount && digit != '.') {
|
||||
var amountSplitByComma = vm.amount.split('.');
|
||||
if (amountSplitByComma.length > 1 && amountSplitByComma[1].length >= LENGTH_AFTER_COMMA_EXPRESSION_LIMIT) return;
|
||||
if (amountSplitByComma.length == 1 && amountSplitByComma[0].length >= LENGTH_BEFORE_COMMA_EXPRESSION_LIMIT) return;
|
||||
}
|
||||
|
||||
if ($scope.amountModel.amount && $scope.amountModel.amount.length >= LENGTH_EXPRESSION_LIMIT) return;
|
||||
if ($scope.amountModel.amount.indexOf('.') > -1 && digit == '.') return;
|
||||
if ($scope.amountModel.amount == '0' && digit == '0') return;
|
||||
if (availableUnits[unitIndex].isFiat && $scope.amountModel.amount.indexOf('.') > -1 && $scope.amountModel.amount[$scope.amountModel.amount.indexOf('.') + 2]) return;
|
||||
if (vm.amount && vm.amount.length >= LENGTH_EXPRESSION_LIMIT) return;
|
||||
if (vm.amount.indexOf('.') > -1 && digit == '.') return;
|
||||
if (vm.amount == '0' && digit == '0') return;
|
||||
if (availableUnits[unitIndex].isFiat && vm.amount.indexOf('.') > -1 && vm.amount[vm.amount.indexOf('.') + 2]) return;
|
||||
|
||||
if ($scope.amountModel.amount == '0' && digit != '.') {
|
||||
$scope.amountModel.amount = '';
|
||||
if (vm.amount == '0' && digit != '.') {
|
||||
vm.amount = '';
|
||||
}
|
||||
|
||||
if ($scope.amountModel.amount == '' && digit == '.') {
|
||||
$scope.amountModel.amount = '0';
|
||||
if (vm.amount == '' && digit == '.') {
|
||||
vm.amount = '0';
|
||||
}
|
||||
|
||||
$scope.amountModel.amount = ($scope.amountModel.amount + digit).replace('..', '.');
|
||||
checkFontSize();
|
||||
$scope.processAmount();
|
||||
};
|
||||
vm.amount = (vm.amount + digit).replace('..', '.');
|
||||
processAmount();
|
||||
}
|
||||
|
||||
$scope.pushOperator = function(operator) {
|
||||
if (!$scope.amountModel.amount || $scope.amountModel.amount.length == 0) return;
|
||||
$scope.amountModel.amount = _pushOperator($scope.amountModel.amount);
|
||||
function pushOperator(operator) {
|
||||
if (!vm.amount || vm.amount.length == 0) return;
|
||||
vm.amount = pushOperator(vm.amount);
|
||||
|
||||
function _pushOperator(val) {
|
||||
function pushOperator(val) {
|
||||
if (!isOperator(lodash.last(val))) {
|
||||
return val + operator;
|
||||
} else {
|
||||
return val.slice(0, -1) + operator;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function isOperator(val) {
|
||||
var regex = /[\/\-\+\x\*]/;
|
||||
return regex.test(val);
|
||||
};
|
||||
}
|
||||
|
||||
function isExpression(val) {
|
||||
var regex = /^\.?\d+(\.?\d+)?([\/\-\+\*x]\d?\.?\d+)+$/;
|
||||
return regex.test(val);
|
||||
};
|
||||
}
|
||||
|
||||
$scope.removeDigit = function() {
|
||||
$scope.amountModel.amount = ($scope.amountModel.amount).toString().slice(0, -1);
|
||||
$scope.processAmount();
|
||||
checkFontSize();
|
||||
};
|
||||
function removeDigit() {
|
||||
vm.amount = (vm.amount).toString().slice(0, -1);
|
||||
processAmount();
|
||||
}
|
||||
|
||||
$scope.resetAmount = function() {
|
||||
$scope.amountModel.amount = $scope.alternativeAmount = $scope.globalResult = '';
|
||||
$scope.allowSend = false;
|
||||
checkFontSize();
|
||||
};
|
||||
function resetAmount() {
|
||||
vm.amount = vm.alternativeAmount = vm.globalResult = '0';
|
||||
vm.allowSend = false;
|
||||
}
|
||||
|
||||
|
||||
$scope.openPopup = function() {
|
||||
function openPopup() {
|
||||
$ionicModal.fromTemplateUrl('views/modals/altCurrency.html', {
|
||||
scope: $scope
|
||||
}).then(function(modal) {
|
||||
$scope.altCurrencyModal = modal;
|
||||
$scope.altCurrencyModal.show();
|
||||
altCurrencyModal = modal;
|
||||
altCurrencyModal.show();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.close = function() {
|
||||
$scope.altCurrencyModal.remove();
|
||||
$scope.altCurrencyModal = false;
|
||||
};
|
||||
function close() {
|
||||
altCurrencyModal.remove();
|
||||
altCurrencyModal = null;
|
||||
}
|
||||
|
||||
$scope.processAmount = function() {
|
||||
var formatedValue = format($scope.amountModel.amount);
|
||||
function processAmount() {
|
||||
var formatedValue = format(vm.amount);
|
||||
var result = evaluate(formatedValue);
|
||||
|
||||
var amountInCrypto = 0;
|
||||
|
||||
if (lodash.isNumber(result)) {
|
||||
$scope.globalResult = isExpression($scope.amountModel.amount) ? '= ' + processResult(result) : '';
|
||||
vm.globalResult = isExpression(vm.amount) ? '= ' + processResult(result) : '';
|
||||
|
||||
if (availableUnits[unitIndex].isFiat) {
|
||||
|
||||
var a = fromFiat(result);
|
||||
if (a) {
|
||||
$scope.alternativeAmount = txFormatService.formatAmount(a * unitToSatoshi, true);
|
||||
$scope.allowSend = lodash.isNumber(a) && a > 0
|
||||
&& (!$scope.shapeshiftOrderId
|
||||
|| (a >= $scope.minShapeshiftAmount && a <= $scope.maxShapeshiftAmount));
|
||||
amountInCrypto = a;
|
||||
var amountInSatoshis = a * unitToSatoshi;
|
||||
vm.fundsAreInsufficient = !!passthroughParams.fromWalletId
|
||||
&& availableSatoshis !== null
|
||||
&& availableSatoshis < amountInSatoshis;
|
||||
|
||||
vm.alternativeAmount = txFormatService.formatAmount(amountInSatoshis, true);
|
||||
vm.allowSend = lodash.isNumber(a)
|
||||
&& a > 0
|
||||
&& (!vm.minAmount || a >= vm.minAmount)
|
||||
&& (!vm.maxAmount || a <= vm.maxAmount)
|
||||
&& !vm.fundsAreInsufficient;
|
||||
} else {
|
||||
if (result) {
|
||||
$scope.alternativeAmount = 'N/A';
|
||||
vm.alternativeAmount = 'N/A';
|
||||
} else {
|
||||
$scope.alternativeAmount = null;
|
||||
vm.alternativeAmount = null;
|
||||
}
|
||||
$scope.allowSend = false;
|
||||
vm.fundsAreInsufficient = false;
|
||||
vm.allowSend = false;
|
||||
}
|
||||
} else {
|
||||
$scope.alternativeAmount = $filter('formatFiatAmount')(toFiat(result));
|
||||
$scope.allowSend = lodash.isNumber(result) && result > 0
|
||||
&& (!$scope.shapeshiftOrderId
|
||||
|| (result >= $scope.minShapeshiftAmount && result <= $scope.maxShapeshiftAmount));
|
||||
amountInCrypto = result;
|
||||
vm.fundsAreInsufficient = passthroughParams.fromWalletId
|
||||
&& availableSatoshis !== null
|
||||
&& availableSatoshis < result * unitToSatoshi;
|
||||
|
||||
vm.alternativeAmount = $filter('formatFiatAmount')(toFiat(result));
|
||||
vm.allowSend = lodash.isNumber(result)
|
||||
&& result > 0
|
||||
&& (!vm.minAmount || result >= vm.minAmount)
|
||||
&& (!vm.maxAmount || result <= vm.maxAmount)
|
||||
&& !vm.fundsAreInsufficient;
|
||||
}
|
||||
|
||||
} else {
|
||||
vm.fundsAreInsufficient = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (vm.fundsAreInsufficient) {
|
||||
vm.errorMessage = gettextCatalog.getString('Not enough available funds');
|
||||
|
||||
} else if (amountInCrypto && vm.thirdParty && vm.thirdParty.id === 'shapeshift') {
|
||||
if (amountInCrypto < vm.minAmount) {
|
||||
vm.errorMessage = gettextCatalog.getString('Amount is below minimum');
|
||||
|
||||
} else if (amountInCrypto > vm.maxAmount) {
|
||||
vm.errorMessage = gettextCatalog.getString('Amount is above maximum');
|
||||
|
||||
} else {
|
||||
vm.errorMessage = '';
|
||||
}
|
||||
} else {
|
||||
vm.errorMessage = '';
|
||||
}
|
||||
}
|
||||
|
||||
function processResult(val) {
|
||||
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, fiatCode, availableUnits[altUnitIndex].id) * satToUnit).toFixed(unitDecimals));
|
||||
};
|
||||
}
|
||||
|
||||
function toFiat(val) {
|
||||
if (!rateService.getRate(fiatCode)) return;
|
||||
|
||||
return parseFloat((rateService.toFiat(val * unitToSatoshi, fiatCode, availableUnits[unitIndex].id)).toFixed(2));
|
||||
};
|
||||
}
|
||||
|
||||
function evaluate(val) {
|
||||
var result;
|
||||
|
|
@ -432,7 +431,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
}
|
||||
if (!lodash.isFinite(result)) return 0;
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
function format(val) {
|
||||
if (!val) return;
|
||||
|
|
@ -442,92 +441,39 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
if (isOperator(lodash.last(val))) result = result.slice(0, -1);
|
||||
|
||||
return result.replace('x', '*');
|
||||
};
|
||||
}
|
||||
|
||||
$scope.finish = function() {
|
||||
function finish() {
|
||||
var unit = availableUnits[unitIndex];
|
||||
var uiAmount = evaluate(format(vm.amount));
|
||||
|
||||
function finish() {
|
||||
var unit = availableUnits[unitIndex];
|
||||
var _amount = evaluate(format($scope.amountModel.amount));
|
||||
var coin = unit.id;
|
||||
if (unit.isFiat) {
|
||||
coin = availableUnits[altUnitIndex].id;
|
||||
}
|
||||
var satoshis = 0;
|
||||
if (unit.isFiat) {
|
||||
satoshis = (fromFiat(uiAmount) * unitToSatoshi).toFixed(0);
|
||||
} else {
|
||||
satoshis = (uiAmount * unitToSatoshi).toFixed(0);
|
||||
}
|
||||
|
||||
if ($scope.nextStep) {
|
||||
$state.transitionTo($scope.nextStep, {
|
||||
id: _id,
|
||||
amount: $scope.useSendMax ? null : _amount,
|
||||
currency: unit.id.toUpperCase(),
|
||||
coin: coin,
|
||||
useSendMax: $scope.useSendMax,
|
||||
fromWalletId: $scope.fromWalletId
|
||||
});
|
||||
} else {
|
||||
var amount = _amount;
|
||||
var confirmData = {
|
||||
amount: useSendMax ? undefined : satoshis,
|
||||
fromWalletId: passthroughParams.fromWalletId,
|
||||
sendMax: useSendMax,
|
||||
toAddress: passthroughParams.toAddress,
|
||||
toWalletId: passthroughParams.toWalletId
|
||||
};
|
||||
|
||||
if (unit.isFiat) {
|
||||
amount = (fromFiat(amount) * unitToSatoshi).toFixed(0);
|
||||
} else {
|
||||
amount = (amount * unitToSatoshi).toFixed(0);
|
||||
}
|
||||
if (vm.thirdParty) {
|
||||
confirmData['thirdParty'] = this.thirdParty;
|
||||
}
|
||||
|
||||
var confirmData = {
|
||||
recipientType: $scope.recipientType,
|
||||
toAmount: amount,
|
||||
toAddress: $scope.toAddress,
|
||||
displayAddress: $scope.displayAddress || $scope.toAddress,
|
||||
toName: $scope.toName,
|
||||
toEmail: $scope.toEmail,
|
||||
toColor: $scope.toColor,
|
||||
coin: coin,
|
||||
useSendMax: $scope.useSendMax,
|
||||
fromWalletId: $scope.fromWalletId
|
||||
};
|
||||
|
||||
if ($scope.shapeshiftOrderId) {
|
||||
var shapeshiftOrderUrl = 'https://www.shapeshift.io/#/status/';
|
||||
shapeshiftOrderUrl += $scope.shapeshiftOrderId;
|
||||
confirmData.description = shapeshiftOrderUrl;
|
||||
confirmData.fromWalletId = $scope.fromWalletId;
|
||||
|
||||
if (confirmData.useSendMax) {
|
||||
var wallet = lodash.find(profileService.getWallets({ coin: coin }),
|
||||
function(w) {
|
||||
return w.id == $scope.fromWalletId;
|
||||
});
|
||||
|
||||
var balance = parseFloat(wallet.cachedBalance.substring(0, wallet.cachedBalance.length-4));
|
||||
if (balance < $scope.minShapeshiftAmount * 1.04) {
|
||||
confirmData.useSendMax = false;
|
||||
confirmData.toAmount = $scope.minShapeshiftAmount * unitToSatoshi;
|
||||
} else if (balance > $scope.maxShapeshiftAmount) {
|
||||
confirmData.useSendMax = false;
|
||||
confirmData.toAmount = $scope.maxShapeshiftAmount * unitToSatoshi * 0.99;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$state.transitionTo('tabs.send.confirm', confirmData);
|
||||
}
|
||||
sendFlowService.pushState(confirmData);
|
||||
if (!confirmData.fromWalletId) {
|
||||
$state.transitionTo('tabs.paymentRequest.confirm', confirmData);
|
||||
} else {
|
||||
$state.transitionTo('tabs.send.review', confirmData);
|
||||
$scope.useSendMax = null;
|
||||
}
|
||||
|
||||
if ($scope.showWarningMessage) {
|
||||
var u = $scope.unit == 'BCH' || $scope.unit == 'BTC' ? $scope.unit : $scope.alternativeUnit;
|
||||
var message = 'Are you sure you want to send ' + u.toUpperCase() + '?';
|
||||
popupService.showConfirm(message, '', 'Yes', 'No', function(res) {
|
||||
if (!res) {
|
||||
$scope.useSendMax = null;
|
||||
return;
|
||||
};
|
||||
finish();
|
||||
});
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Currency
|
||||
|
|
@ -546,7 +492,7 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
{isoCode: 'CNY', order: 7},
|
||||
{isoCode: 'KRW', order: 8},
|
||||
{isoCode: 'HKD', order: 9},
|
||||
]
|
||||
];
|
||||
|
||||
function initCurrencies() {
|
||||
var unusedCurrencyList = [{
|
||||
|
|
@ -562,10 +508,10 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
}];
|
||||
rateService.whenAvailable(function() {
|
||||
|
||||
$scope.listComplete = false;
|
||||
vm.listComplete = false;
|
||||
|
||||
var idx = lodash.indexBy(unusedCurrencyList, 'isoCode');
|
||||
var idx2 = lodash.indexBy($scope.lastUsedAltCurrencyList, 'isoCode');
|
||||
var idx2 = lodash.indexBy(lastUsedAltCurrencyList, 'isoCode');
|
||||
var idx3 = lodash.indexBy(popularCurrencyList, 'isoCode');
|
||||
var alternatives = rateService.listAlternatives(true);
|
||||
|
||||
|
|
@ -578,8 +524,10 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
}
|
||||
});
|
||||
|
||||
$scope.altCurrencyList = completeAlternativeList.slice(0, 10);
|
||||
$scope.lastUsedPopularList = lodash.unique(lodash.union($scope.lastUsedAltCurrencyList, popularCurrencyList), 'isoCode');
|
||||
vm.altCurrencyList = completeAlternativeList.slice(0, 10);
|
||||
vm.lastUsedPopularList = lodash.unique(lodash.union(lastUsedAltCurrencyList, popularCurrencyList), 'isoCode');
|
||||
|
||||
rateService.updateRates();
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
|
|
@ -587,19 +535,19 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
});
|
||||
}
|
||||
|
||||
$scope.loadMore = function() {
|
||||
function loadMore() {
|
||||
$timeout(function() {
|
||||
$scope.altCurrencyList = completeAlternativeList.slice(0, next);
|
||||
vm.altCurrencyList = completeAlternativeList.slice(0, next);
|
||||
next += 10;
|
||||
$scope.listComplete = $scope.altCurrencyList.length >= completeAlternativeList.length;
|
||||
vm.listComplete = vm.altCurrencyList.length >= completeAlternativeList.length;
|
||||
$scope.$broadcast('scroll.infiniteScrollComplete');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.findCurrency = function(search) {
|
||||
function findCurrency(search) {
|
||||
if (!search) initCurrencies();
|
||||
var list = lodash.unique(lodash.union(completeAlternativeList, lodash.union($scope.lastUsedAltCurrencyList, popularCurrencyList)), 'isoCode');
|
||||
$scope.altCurrencyList = lodash.filter(list, function(item) {
|
||||
var list = lodash.unique(lodash.union(completeAlternativeList, lodash.union(lastUsedAltCurrencyList, popularCurrencyList)), 'isoCode');
|
||||
vm.altCurrencyList = lodash.filter(list, function(item) {
|
||||
var val = item.name
|
||||
var val2 = item.isoCode;
|
||||
return lodash.includes(val.toLowerCase(), search.toLowerCase()) || lodash.includes(val2.toLowerCase(), search.toLowerCase());
|
||||
|
|
@ -607,9 +555,9 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
$scope.save = function(newAltCurrency) {
|
||||
function save(newAltCurrency) {
|
||||
var opts = {
|
||||
wallet: {
|
||||
settings: {
|
||||
|
|
@ -629,8 +577,63 @@ angular.module('copayApp.controllers').controller('amountController', function($
|
|||
availableUnits[altUnitIndex].name = newAltCurrency.isoCode;
|
||||
availableUnits[altUnitIndex].shortName = newAltCurrency.isoCode;
|
||||
fiatCode = newAltCurrency.isoCode;
|
||||
updateAvailableFundsStringIfNeeded();
|
||||
updateUnitUI();
|
||||
$scope.close();
|
||||
close();
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function updateAvailableFundsStringIfNeeded() {
|
||||
if (passthroughParams.fromWalletId && availableSatoshis !== null) {
|
||||
availableFundsInFiat = '';
|
||||
vm.availableFunds = availableFundsInCrypto;
|
||||
|
||||
if (availableUnits[unitIndex].isFiat) {
|
||||
var coin = availableUnits[altUnitIndex].id;
|
||||
txFormatService.formatAlternativeStr(coin, availableSatoshis, function formatCallback(formatted){
|
||||
if (formatted) {
|
||||
availableFundsInFiat = formatted;
|
||||
|
||||
$scope.$apply(function() {
|
||||
vm.availableFunds = availableFundsInFiat;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateAvailableFundsFromWallet(wallet) {
|
||||
if (wallet.status && wallet.status.isValid) {
|
||||
availableFundsInCrypto = wallet.status.spendableBalanceStr;
|
||||
availableSatoshis = wallet.status.spendableAmount;
|
||||
if (wallet.status.alternativeBalanceAvailable) {
|
||||
availableFundsInFiat = wallet.status.spendableBalanceAlternative + ' ' + wallet.status.alternativeIsoCode;
|
||||
} else {
|
||||
availableFundsInFiat = '';
|
||||
}
|
||||
|
||||
} else if (wallet.cachedStatus && wallet.status.isValid) {
|
||||
|
||||
if (wallet.cachedStatus.alternativeBalanceAvailable) {
|
||||
availableFundsInFiat = wallet.cachedStatus.spendableBalanceAlternative + ' ' + wallet.cachedStatus.alternativeIsoCode;
|
||||
} else {
|
||||
availableFundsInFiat = '';
|
||||
}
|
||||
availableFundsInCrypto = wallet.cachedStatus.spendableBalanceStr;
|
||||
availableSatoshis = wallet.cachedStatus.spendableAmount;
|
||||
|
||||
} else {
|
||||
|
||||
availableFundsInFiat = '';
|
||||
availableFundsInCrypto = '';
|
||||
availableSatoshis = null;
|
||||
}
|
||||
|
||||
if (availableUnits[unitIndex].isFiat) {
|
||||
vm.availableFunds = availableFundsInFiat || availableFundsInCrypto;
|
||||
} else {
|
||||
vm.availableFunds = availableFundsInCrypto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
101
src/js/controllers/amount.spec.js
Normal file
101
src/js/controllers/amount.spec.js
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
describe('amountController', function(){
|
||||
var configCache,
|
||||
configService,
|
||||
$controller,
|
||||
$ionicHistory,
|
||||
$rootScope,
|
||||
platformInfo,
|
||||
profileService,
|
||||
rateService,
|
||||
$stateParams;
|
||||
|
||||
|
||||
|
||||
beforeEach(function(){
|
||||
module('ngLodash');
|
||||
module('copayApp.controllers');
|
||||
|
||||
configCache = {
|
||||
wallet: {
|
||||
settings: {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
configService = jasmine.createSpyObj(['getDefaults','getSync']);
|
||||
configService.getDefaults.and.returnValue({
|
||||
bitcoinCashAlias: 'bch',
|
||||
bitcoinAlias: 'btc'
|
||||
});
|
||||
configService.getSync.and.returnValue(configCache);
|
||||
|
||||
$ionicHistory = jasmine.createSpyObj(['backView']);
|
||||
|
||||
platformInfo = {
|
||||
isChromeApp: false,
|
||||
isAndroid: false,
|
||||
isIos: true
|
||||
};
|
||||
|
||||
profileService = jasmine.createSpyObj(['getWallets']);
|
||||
|
||||
rateService = jasmine.createSpyObj(['fromFiat', 'whenAvailable']);
|
||||
|
||||
$stateParams = {};
|
||||
|
||||
inject(function(_$controller_, _$rootScope_){
|
||||
// The injector unwraps the underscores (_) from around the parameter names when matching
|
||||
$controller = _$controller_;
|
||||
$rootScope = _$rootScope_;
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('receives fromWalletId and toAddress.', function() {
|
||||
|
||||
var backView = {
|
||||
stateName: 'ignoreme'
|
||||
};
|
||||
$ionicHistory.backView.and.returnValue(backView);
|
||||
profileService.getWallets.and.returnValue([{}]);
|
||||
rateService.fromFiat.and.returnValue(12); // satoshis or coins?
|
||||
|
||||
var $scope = $rootScope.$new();
|
||||
|
||||
|
||||
var amountController = $controller('amountController', {
|
||||
configService: configService,
|
||||
gettextCatalog: {},
|
||||
$ionicHistory: $ionicHistory,
|
||||
$ionicModal: {},
|
||||
$ionicScrollDelegate: {},
|
||||
nodeWebkitService: {},
|
||||
ongoingProcess: {},
|
||||
platformInfo: platformInfo,
|
||||
profileService: profileService,
|
||||
popupService: {},
|
||||
rateService: rateService,
|
||||
$scope: $scope,
|
||||
$state: {},
|
||||
$stateParams: $stateParams,
|
||||
txFormatService: {},
|
||||
walletService: {}
|
||||
});
|
||||
|
||||
var data = {
|
||||
stateParams: {
|
||||
fromWalletId: 'fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b',
|
||||
toAddress: 'qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s'
|
||||
}
|
||||
};
|
||||
$scope.$emit('$ionicView.beforeEnter', data);
|
||||
|
||||
expect($scope.fromWalletId).toBe('fd56c1e7-e3ac-4fd9-8afc-27b9c1b3718b');
|
||||
expect($scope.toAddress).toBe('qrup46avn8t466xxwlzs4qelht7cnwvesv2e29wf7s');
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('backupController',
|
||||
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, lodash, profileService, bwcService, walletService, ongoingProcess, popupService, gettextCatalog, $ionicModal, firebaseEventsService) {
|
||||
function($scope, $timeout, $log, $state, $stateParams, $ionicHistory, lodash, profileService, bwcService, walletService, ongoingProcess, popupService, gettextCatalog, $ionicModal) {
|
||||
|
||||
if ($state.current.name == 'onboarding.backup') {
|
||||
$scope.onboarding = true;
|
||||
|
|
@ -89,7 +89,8 @@ angular.module('copayApp.controllers').controller('backupController',
|
|||
$scope.setFlow(2);
|
||||
})
|
||||
} else {
|
||||
firebaseEventsService.logEvent('backed_up_wallet');
|
||||
|
||||
//firebaseEventsService.logEvent('backed_up_wallet');
|
||||
openConfirmBackupModal();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
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, firebaseEventsService, soundService) {
|
||||
angular.module('copayApp.controllers').controller('confirmController', function($rootScope, $scope, $interval, $timeout, $ionicScrollDelegate, $ionicLoading, ionicToast, addressbookService, gettextCatalog, walletService, platformInfo, lodash, configService, $state, $log, profileService, bitcore, bitcoreCash, txFormatService, ongoingProcess, $ionicModal, popupService, $ionicHistory, $ionicConfig, feeService, bitcoinCashJsService, bwcError, txConfirmNotification, soundService, clipboardService) {
|
||||
|
||||
var countDown = null;
|
||||
var FEE_TOO_HIGH_LIMIT_PER = 15;
|
||||
|
||||
var tx = {};
|
||||
var lastTxId = "";
|
||||
|
||||
// Config Related values
|
||||
var config = configService.getSync();
|
||||
var walletConfig = config.wallet;
|
||||
var unitToSatoshi = walletConfig.settings.unitToSatoshi;
|
||||
var unitDecimals = walletConfig.settings.unitDecimals;
|
||||
var satToUnit = 1 / unitToSatoshi;
|
||||
var configFeeLevel = walletConfig.settings.feeLevel ? walletConfig.settings.feeLevel : 'normal';
|
||||
|
||||
|
||||
// Platform info
|
||||
var isChromeApp = platformInfo.isChromeApp;
|
||||
var isCordova = platformInfo.isCordova;
|
||||
var isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
|
||||
|
||||
//custom fee flag
|
||||
var usingCustomFee = false;
|
||||
|
|
@ -31,6 +26,16 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
}, 10);
|
||||
}
|
||||
|
||||
$scope.shareTransaction = function() {
|
||||
var explorerTxUrl = 'https://explorer.bitcoin.com/'+tx.coin+'/tx/'+lastTxId;
|
||||
if (platformInfo.isCordova) {
|
||||
var text = 'Take a look at this Bitcoin transaction here: '+explorerTxUrl;
|
||||
window.plugins.socialsharing.share(text, null, null, null);
|
||||
} else {
|
||||
ionicToast.show(gettextCatalog.getString('Copied to clipboard'), 'bottom', false, 3000);
|
||||
clipboardService.copyToClipboard(explorerTxUrl);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showWalletSelector = function() {
|
||||
$scope.walletSelector = true;
|
||||
|
|
@ -45,7 +50,6 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
$ionicConfig.views.swipeBackEnabled(false);
|
||||
});
|
||||
|
||||
|
||||
function exitWithError(err) {
|
||||
$log.info('Error setting wallet selector:' + err);
|
||||
popupService.showAlert(gettextCatalog.getString(), bwcError.msg(err), function() {
|
||||
|
|
@ -68,112 +72,108 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
});
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
var setWalletSelector = function(coin, network, minAmount, cb) {
|
||||
|
||||
function setWalletSelector(coin, network, minAmount, cb) {
|
||||
// no min amount? (sendMax) => look for no empty wallets
|
||||
minAmount = minAmount || 1;
|
||||
|
||||
// no min amount? (sendMax) => look for no empty wallets
|
||||
minAmount = minAmount || 1;
|
||||
$scope.wallets = profileService.getWallets({
|
||||
onlyComplete: true,
|
||||
network: network,
|
||||
coin: coin
|
||||
});
|
||||
|
||||
$scope.wallets = profileService.getWallets({
|
||||
onlyComplete: true,
|
||||
network: network,
|
||||
coin: coin
|
||||
if (tx.fromWalletId) {
|
||||
$scope.wallets = lodash.filter($scope.wallets, function (w) {
|
||||
return w.id == tx.fromWalletId;
|
||||
});
|
||||
|
||||
if (tx.fromWalletId) {
|
||||
$scope.wallets = lodash.filter($scope.wallets, function(w) {
|
||||
return w.id == tx.fromWalletId;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!$scope.wallets || !$scope.wallets.length) {
|
||||
setNoWallet(gettextCatalog.getString('No wallets available'), true);
|
||||
return cb();
|
||||
}
|
||||
|
||||
var filteredWallets = [];
|
||||
var index = 0;
|
||||
var walletsUpdated = 0;
|
||||
|
||||
lodash.each($scope.wallets, function(w) {
|
||||
walletService.getStatus(w, {}, function(err, status) {
|
||||
if (err || !status) {
|
||||
$log.error(err);
|
||||
} else {
|
||||
walletsUpdated++;
|
||||
w.status = status;
|
||||
|
||||
if (!status.availableBalanceSat)
|
||||
$log.debug('No balance available in: ' + w.name);
|
||||
|
||||
if (status.availableBalanceSat > minAmount) {
|
||||
filteredWallets.push(w);
|
||||
}
|
||||
}
|
||||
|
||||
if (++index == $scope.wallets.length) {
|
||||
if (!walletsUpdated)
|
||||
return cb('Could not update any wallet');
|
||||
|
||||
if (lodash.isEmpty(filteredWallets)) {
|
||||
setNoWallet(gettextCatalog.getString('Insufficient confirmed funds'), true);
|
||||
}
|
||||
$scope.wallets = lodash.clone(filteredWallets);
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 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('Invalid address');
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
if (!$scope.wallets || !$scope.wallets.length) {
|
||||
setNoWallet(gettextCatalog.getString('No wallets available'), true);
|
||||
return cb();
|
||||
}
|
||||
|
||||
var filteredWallets = [];
|
||||
var index = 0;
|
||||
var walletsUpdated = 0;
|
||||
|
||||
lodash.each($scope.wallets, function (w) {
|
||||
walletService.getStatus(w, {}, function (err, status) {
|
||||
if (err || !status) {
|
||||
$log.error(err);
|
||||
} else {
|
||||
walletsUpdated++;
|
||||
w.status = status;
|
||||
|
||||
if (!status.availableBalanceSat)
|
||||
$log.debug('No balance available in: ' + w.name);
|
||||
|
||||
if (status.availableBalanceSat > minAmount) {
|
||||
filteredWallets.push(w);
|
||||
}
|
||||
}
|
||||
|
||||
if (++index == $scope.wallets.length) {
|
||||
if (!walletsUpdated)
|
||||
return cb('Could not update any wallet');
|
||||
|
||||
if (lodash.isEmpty(filteredWallets)) {
|
||||
setNoWallet(gettextCatalog.getString('Insufficient confirmed funds'), true);
|
||||
}
|
||||
$scope.wallets = lodash.clone(filteredWallets);
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.getContacts = function(addr) {
|
||||
addressbookService.list(function(err, ab) {
|
||||
if (err) $log.error(err);
|
||||
|
||||
$scope.hasContacts = lodash.isEmpty(ab) ? false : true;
|
||||
if (!$scope.hasContacts) return cb();
|
||||
|
||||
var completeContacts = [];
|
||||
lodash.each(ab, function(v, k) {
|
||||
completeContacts.push({
|
||||
name: lodash.isObject(v) ? v.name : v,
|
||||
address: k,
|
||||
email: lodash.isObject(v) ? v.email : null,
|
||||
recipientType: 'contact',
|
||||
coin: v.coin,
|
||||
displayCoin: (v.coin == 'bch'
|
||||
? (config.bitcoinCashAlias || defaults.bitcoinCashAlias)
|
||||
: (config.bitcoinAlias || defaults.bitcoinAlias)).toUpperCase()
|
||||
});
|
||||
});
|
||||
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
$scope.fromWallet = profileService.getWallet(data.stateParams.fromWalletId); // Wallet to send from
|
||||
|
||||
|
||||
// Grab stateParams
|
||||
tx = {
|
||||
toAmount: parseInt(data.stateParams.toAmount),
|
||||
amount: parseInt(data.stateParams.amount),
|
||||
sendMax: data.stateParams.useSendMax == 'true' ? true : false,
|
||||
fromWalletId: data.stateParams.fromWalletId,
|
||||
toAddress: data.stateParams.toAddress,
|
||||
displayAddress: data.stateParams.displayAddress,
|
||||
description: data.stateParams.description,
|
||||
paypro: data.stateParams.paypro,
|
||||
|
||||
feeLevel: configFeeLevel,
|
||||
spendUnconfirmed: walletConfig.spendUnconfirmed,
|
||||
|
||||
// Vanity tx info (not in the real tx)
|
||||
recipientType: data.stateParams.recipientType || null,
|
||||
toName: data.stateParams.toName,
|
||||
toEmail: data.stateParams.toEmail,
|
||||
toColor: data.stateParams.toColor,
|
||||
network: networkName,
|
||||
coin: data.stateParams.coin,
|
||||
recipientType: $scope.recipientType || null,
|
||||
toName: null,
|
||||
toEmail: null,
|
||||
toColor: null,
|
||||
network: false,
|
||||
coin: $scope.fromWallet.coin,
|
||||
txp: {},
|
||||
};
|
||||
|
||||
|
|
@ -182,18 +182,71 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
tx.feeRate = parseInt(data.stateParams.requiredFeeRate);
|
||||
}
|
||||
|
||||
if (tx.coin && tx.coin == 'bch') {
|
||||
if (tx.coin && tx.coin === 'bch') {
|
||||
tx.feeLevel = 'normal';
|
||||
}
|
||||
|
||||
var B = data.stateParams.coin === 'bch' ? bitcoreCash : bitcore;
|
||||
var networkName;
|
||||
$scope.recipientType = null;
|
||||
try {
|
||||
if (data.stateParams.toWalletId) { // There is a toWalletId, so we presume this is a wallet-to-wallet transfer
|
||||
$scope.recipientType = 'wallet'; // set transaction type to wallet-to-wallet
|
||||
$ionicLoading.show();
|
||||
|
||||
var toWallet = profileService.getWallet(data.stateParams.toWalletId);
|
||||
tx.toColor = toWallet.color;
|
||||
tx.toName = toWallet.name;
|
||||
|
||||
// We need an address to send to, so we ask the walletService to create a new address for the toWallet.
|
||||
walletService.getAddress(toWallet, true, function (err, addr) {
|
||||
$ionicLoading.hide();
|
||||
tx.toAddress = addr;
|
||||
networkName = (new B.Address(tx.toAddress)).network.name;
|
||||
tx.network = networkName;
|
||||
setupTx(tx);
|
||||
});
|
||||
} else { // This is a Wallet-to-address transfer
|
||||
networkName = (new B.Address(tx.toAddress)).network.name;
|
||||
tx.network = networkName;
|
||||
setupTx(tx);
|
||||
}
|
||||
} catch (e) {
|
||||
var message = gettextCatalog.getString('Invalid address');
|
||||
popupService.showAlert(null, message, function () {
|
||||
$ionicHistory.nextViewOptions({
|
||||
disableAnimate: true,
|
||||
historyRoot: true
|
||||
});
|
||||
$state.go('tabs.send').then(function () {
|
||||
$ionicHistory.clearHistory();
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
var setupTx = function(tx) {
|
||||
if (tx.coin === 'bch') {
|
||||
tx.displayAddress = bitcoinCashJsService.readAddress(tx.toAddress).cashaddr;
|
||||
} else {
|
||||
tx.displayAddress = entry.address;
|
||||
}
|
||||
|
||||
addressbookService.get(tx.coin+tx.toAddress, function(err, addr) { // Check if the recipient is a contact
|
||||
if (!err && addr) {
|
||||
tx.toName = addr.name;
|
||||
tx.toEmail = addr.email;
|
||||
tx.recipientType = 'contact';
|
||||
}
|
||||
});
|
||||
|
||||
// Other Scope vars
|
||||
$scope.isCordova = isCordova;
|
||||
$scope.isWindowsPhoneApp = isWindowsPhoneApp;
|
||||
$scope.showAddress = false;
|
||||
|
||||
$scope.walletSelectorTitle = gettextCatalog.getString('Send from');
|
||||
|
||||
setWalletSelector(tx.coin, tx.network, tx.toAmount, function(err) {
|
||||
setWalletSelector(tx.coin, tx.network, tx.amount, function(err) {
|
||||
if (err) {
|
||||
return exitWithError('Could not update wallets');
|
||||
}
|
||||
|
|
@ -207,7 +260,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
|
||||
$scope.displayBalanceAsFiat = walletConfig.settings.priceDisplay === 'fiat';
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
function getSendMaxInfo(tx, wallet, cb) {
|
||||
|
|
@ -231,7 +284,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
return setSendError(msg);
|
||||
}
|
||||
|
||||
if (tx.toAmount > Number.MAX_SAFE_INTEGER) {
|
||||
if (tx.amount > Number.MAX_SAFE_INTEGER) {
|
||||
var msg = gettextCatalog.getString('Amount too big');
|
||||
$log.warn(msg);
|
||||
return setSendError(msg);
|
||||
|
|
@ -241,7 +294,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
|
||||
txp.outputs = [{
|
||||
'toAddress': tx.toAddress,
|
||||
'amount': tx.toAmount,
|
||||
'amount': tx.amount,
|
||||
'message': tx.description
|
||||
}];
|
||||
|
||||
|
|
@ -280,13 +333,13 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
$scope.tx = tx;
|
||||
|
||||
function updateAmount() {
|
||||
if (!tx.toAmount) return;
|
||||
if (!tx.amount) return;
|
||||
|
||||
// Amount
|
||||
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.toAmount);
|
||||
tx.amountStr = txFormatService.formatAmountStr(wallet.coin, tx.amount);
|
||||
tx.amountValueStr = tx.amountStr.split(' ')[0];
|
||||
tx.amountUnitStr = tx.amountStr.split(' ')[1];
|
||||
txFormatService.formatAlternativeStr(wallet.coin, tx.toAmount, function(v) {
|
||||
txFormatService.formatAlternativeStr(wallet.coin, tx.amount, function(v) {
|
||||
var parts = v.split(' ');
|
||||
tx.alternativeAmountStr = v;
|
||||
tx.alternativeAmountValueStr = parts[0];
|
||||
|
|
@ -342,7 +395,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
}
|
||||
|
||||
tx.sendMaxInfo = sendMaxInfo;
|
||||
tx.toAmount = tx.sendMaxInfo.amount;
|
||||
tx.amount = tx.sendMaxInfo.amount;
|
||||
updateAmount();
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
$timeout(function() {
|
||||
|
|
@ -393,7 +446,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
function useSelectedWallet() {
|
||||
|
||||
if (!$scope.useSendMax) {
|
||||
showAmount(tx.toAmount);
|
||||
showAmount(tx.amount);
|
||||
}
|
||||
|
||||
$scope.onWalletSelect($scope.wallet);
|
||||
|
|
@ -402,19 +455,19 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
function setButtonText(isMultisig, isPayPro) {
|
||||
|
||||
if (isPayPro) {
|
||||
if (isCordova && !isWindowsPhoneApp) {
|
||||
if (isCordova) {
|
||||
$scope.buttonText = gettextCatalog.getString('Slide to pay');
|
||||
} else {
|
||||
$scope.buttonText = gettextCatalog.getString('Click to pay');
|
||||
}
|
||||
} else if (isMultisig) {
|
||||
if (isCordova && !isWindowsPhoneApp) {
|
||||
if (isCordova) {
|
||||
$scope.buttonText = gettextCatalog.getString('Slide to accept');
|
||||
} else {
|
||||
$scope.buttonText = gettextCatalog.getString('Click to accept');
|
||||
}
|
||||
} else {
|
||||
if (isCordova && !isWindowsPhoneApp) {
|
||||
if (isCordova) {
|
||||
$scope.buttonText = gettextCatalog.getString('Slide to send');
|
||||
} else {
|
||||
$scope.buttonText = gettextCatalog.getString('Click to send');
|
||||
|
|
@ -422,7 +475,6 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
$scope.toggleAddress = function() {
|
||||
$scope.showAddress = !$scope.showAddress;
|
||||
};
|
||||
|
|
@ -612,6 +664,7 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
txConfirmNotification.subscribe(wallet, {
|
||||
txid: txp.txid
|
||||
});
|
||||
lastTxId = txp.txid;
|
||||
}
|
||||
}, onSendStatusChange);
|
||||
};
|
||||
|
|
@ -643,9 +696,9 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
soundService.play('misc/payment_sent.mp3');
|
||||
}
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("transfer_success", [{
|
||||
"coin": $scope.wallet.coin,
|
||||
|
|
@ -655,8 +708,6 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
}], [channel, "adjust"]);
|
||||
window.BitAnalytics.LogEventHandlers.postEvent(log);
|
||||
|
||||
// Should be removed
|
||||
firebaseEventsService.logEvent('sent_bitcoin', { coin: $scope.wallet.coin });
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 100);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('createController',
|
||||
function($scope, $rootScope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettextCatalog, ledger, trezor, intelTEE, derivationPathHelper, ongoingProcess, walletService, storageService, popupService, appConfigService, pushNotificationsService, firebaseEventsService, $ionicNavBarDelegate) {
|
||||
function($scope, $timeout, $log, lodash, $state, $ionicScrollDelegate, $ionicHistory, profileService, configService, gettextCatalog, ledger, trezor, intelTEE, derivationPathHelper, ongoingProcess, walletService, popupService, appConfigService, pushNotificationsService, $ionicNavBarDelegate) {
|
||||
|
||||
/* For compressed keys, m*73 + n*34 <= 496 */
|
||||
var COPAYER_PAIR_LIMITS = {
|
||||
|
|
@ -268,7 +268,7 @@ angular.module('copayApp.controllers').controller('createController',
|
|||
}, 100);
|
||||
}
|
||||
else {
|
||||
firebaseEventsService.logEvent('wallet_created', { coin: opts.coin });
|
||||
//firebaseEventsService.logEvent('wallet_created', { coin: opts.coin });
|
||||
$state.go('tabs.home');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ angular.module('copayApp.controllers').controller('customAmountController', func
|
|||
}
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
var walletId = data.stateParams.id;
|
||||
var walletId = data.stateParams.toWalletId;
|
||||
|
||||
if (!walletId) {
|
||||
showErrorAndBack('Error', 'No wallet selected');
|
||||
|
|
@ -53,17 +53,26 @@ angular.module('copayApp.controllers').controller('customAmountController', func
|
|||
$scope.address = bchAddresses[$scope.bchAddressType];
|
||||
}
|
||||
|
||||
$scope.coin = data.stateParams.coin;
|
||||
$scope.coin = $scope.wallet.coin;
|
||||
var satoshis = parseInt(data.stateParams.amount, 10);
|
||||
var parsedAmount = txFormatService.parseAmount(
|
||||
$scope.wallet.coin,
|
||||
data.stateParams.amount,
|
||||
data.stateParams.currency);
|
||||
satoshis,
|
||||
'sat');
|
||||
|
||||
// Amount in USD or BTC
|
||||
var amount = parsedAmount.amount;
|
||||
var currency = parsedAmount.currency;
|
||||
$scope.amountUnitStr = parsedAmount.amountUnitStr;
|
||||
|
||||
configService.whenAvailable(function (config) {
|
||||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
|
||||
|
||||
$timeout(function () {
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
|
||||
if (currency != 'BTC' && currency != 'BCH') {
|
||||
// Convert to BTC or BCH
|
||||
var config = configService.getSync().wallet.settings;
|
||||
|
|
|
|||
|
|
@ -76,9 +76,9 @@ angular.module('copayApp.controllers').controller('preferencesNotificationsContr
|
|||
|
||||
emailService.updateEmail(opts);
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("settings_email_notification_toggle", [{
|
||||
"toggle": $scope.emailNotifications.value
|
||||
|
|
|
|||
900
src/js/controllers/review.controller.js
Normal file
900
src/js/controllers/review.controller.js
Normal file
|
|
@ -0,0 +1,900 @@
|
|||
'use strict';
|
||||
|
||||
angular
|
||||
.module('copayApp.controllers')
|
||||
.controller('reviewController', reviewController);
|
||||
|
||||
function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) {
|
||||
var vm = this;
|
||||
|
||||
vm.buttonText = '';
|
||||
vm.destination = {
|
||||
address: '',
|
||||
balanceAmount: '',
|
||||
balanceCurrency: '',
|
||||
coin: '',
|
||||
color: '',
|
||||
currency: '',
|
||||
currencyColor: '',
|
||||
kind: '', // 'address', 'contact', 'wallet'
|
||||
name: ''
|
||||
};
|
||||
vm.feeCrypto = '';
|
||||
vm.feeFiat = '';
|
||||
vm.fiatCurrency = '';
|
||||
vm.feeIsHigh = false;
|
||||
vm.feeLessThanACent = false;
|
||||
vm.isCordova = platformInfo.isCordova;
|
||||
vm.notReadyMessage = '';
|
||||
vm.origin = {
|
||||
balanceAmount: '',
|
||||
balanceCurrency: '',
|
||||
currency: '',
|
||||
currencyColor: '',
|
||||
};
|
||||
vm.originWallet = null;
|
||||
vm.paymentExpired = false;
|
||||
vm.primaryAmount = '';
|
||||
vm.primaryCurrency = '';
|
||||
vm.usingMerchantFee = false;
|
||||
vm.readyToSend = false;
|
||||
vm.remainingTimeStr = '';
|
||||
vm.secondaryAmount = '';
|
||||
vm.secondaryCurrency = '';
|
||||
vm.sendingTitle = gettextCatalog.getString('You are sending');
|
||||
vm.sendStatus = '';
|
||||
vm.showAddress = true;
|
||||
vm.thirdParty = false;
|
||||
vm.wallet = null;
|
||||
vm.memoExpanded = false;
|
||||
|
||||
// Functions
|
||||
vm.goBack = goBack;
|
||||
vm.onSuccessConfirm = onSuccessConfirm;
|
||||
|
||||
var sendFlowData;
|
||||
var config = null;
|
||||
var countDown = null;
|
||||
var defaults = {};
|
||||
var coin = '';
|
||||
var countDown = null;
|
||||
var usingCustomFee = false;
|
||||
var usingMerchantFee = false;
|
||||
var destinationWalletId = '';
|
||||
var originWalletId = '';
|
||||
var priceDisplayIsFiat = true;
|
||||
var satoshis = null;
|
||||
var toAddress = '';
|
||||
var tx = {};
|
||||
var txPayproData = null;
|
||||
var unitFromSat = 0;
|
||||
|
||||
var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15;
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
|
||||
|
||||
|
||||
function onBeforeEnter(event, data) {
|
||||
console.log('walletSelector onBeforeEnter sendflow ', sendFlowService.getState());
|
||||
defaults = configService.getDefaults();
|
||||
sendFlowData = sendFlowService.getState();
|
||||
originWalletId = sendFlowData.fromWalletId;
|
||||
satoshis = parseInt(sendFlowData.amount, 10);
|
||||
toAddress = sendFlowData.toAddress;
|
||||
destinationWalletId = sendFlowData.toWalletId;
|
||||
|
||||
vm.originWallet = profileService.getWallet(originWalletId);
|
||||
vm.origin.currency = vm.originWallet.coin.toUpperCase();
|
||||
coin = vm.originWallet.coin;
|
||||
|
||||
if (sendFlowData.thirdParty) {
|
||||
vm.thirdParty = sendFlowData.thirdParty;
|
||||
handleThirdPartyInitIfBip70();
|
||||
handleThirdPartyInitIfShapeshift();
|
||||
}
|
||||
|
||||
configService.get(function onConfig(err, configCache) {
|
||||
if (err) {
|
||||
$log.err('Error getting config.', err);
|
||||
} else {
|
||||
config = configCache;
|
||||
priceDisplayIsFiat = config.wallet.settings.priceDisplay === 'fiat';
|
||||
vm.origin.currencyColor = (vm.originWallet.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor);
|
||||
console.log("coin", vm.originWallet.coin, vm.origin.currencyColor, config.bitcoinWalletColor, vm.originWallet.coin === 'btc');
|
||||
unitFromSat = 1 / config.wallet.settings.unitToSatoshi;
|
||||
}
|
||||
updateSendAmounts();
|
||||
getOriginWalletBalance(vm.originWallet);
|
||||
handleDestinationAsAddress(toAddress, coin);
|
||||
handleDestinationAsWallet(sendFlowData.toWalletId);
|
||||
createVanityTransaction(data);
|
||||
});
|
||||
}
|
||||
|
||||
vm.approve = function() {
|
||||
|
||||
if (!tx || !vm.originWallet) return;
|
||||
|
||||
if (vm.paymentExpired) {
|
||||
popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.'));
|
||||
vm.sendStatus = '';
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ongoingProcess.set('creatingTx', true, statusChangeHandler);
|
||||
getTxp(lodash.clone(tx), vm.originWallet, false, function(err, txp) {
|
||||
ongoingProcess.set('creatingTx', false, statusChangeHandler);
|
||||
if (err) return;
|
||||
|
||||
// confirm txs for more that 20usd, if not spending/touchid is enabled
|
||||
function confirmTx(cb) {
|
||||
if (walletService.isEncrypted(vm.originWallet))
|
||||
return cb();
|
||||
|
||||
var amountUsd = parseFloat(txFormatService.formatToUSD(vm.originWallet.coin, txp.amount));
|
||||
return cb();
|
||||
};
|
||||
|
||||
function publishAndSign() {
|
||||
if (!vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) {
|
||||
$log.info('No signing proposal: No private key');
|
||||
|
||||
return walletService.onlyPublish(vm.originWallet, txp, function(err) {
|
||||
if (err) setSendError(err);
|
||||
}, statusChangeHandler);
|
||||
}
|
||||
|
||||
walletService.publishAndSign(vm.originWallet, txp, function(err, txp) {
|
||||
if (err) return setSendError(err);
|
||||
if (config.confirmedTxsNotifications && config.confirmedTxsNotifications.enabled) {
|
||||
txConfirmNotification.subscribe(vm.originWallet, {
|
||||
txid: txp.txid
|
||||
});
|
||||
}
|
||||
}, statusChangeHandler);
|
||||
};
|
||||
|
||||
confirmTx(function(nok) {
|
||||
if (nok) {
|
||||
vm.sendStatus = '';
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
publishAndSign();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
vm.chooseFeeLevel = function(tx, wallet) {
|
||||
|
||||
if (wallet.coin == 'bch') return;
|
||||
if (usingMerchantFee) return;
|
||||
|
||||
var scope = $rootScope.$new(true);
|
||||
scope.network = tx.network;
|
||||
scope.feeLevel = tx.feeLevel;
|
||||
scope.noSave = true;
|
||||
scope.coin = vm.originWallet.coin;
|
||||
|
||||
if (usingCustomFee) {
|
||||
scope.customFeePerKB = tx.feeRate;
|
||||
scope.feePerSatByte = tx.feeRate / 1000;
|
||||
}
|
||||
|
||||
$ionicModal.fromTemplateUrl('views/modals/chooseFeeLevel.html', {
|
||||
scope: scope,
|
||||
backdropClickToClose: false,
|
||||
hardwareBackButtonClose: false
|
||||
}).then(function(modal) {
|
||||
scope.chooseFeeLevelModal = modal;
|
||||
scope.openModal();
|
||||
});
|
||||
scope.openModal = function() {
|
||||
scope.chooseFeeLevelModal.show();
|
||||
};
|
||||
|
||||
scope.hideModal = function(newFeeLevel, customFeePerKB) {
|
||||
scope.chooseFeeLevelModal.hide();
|
||||
$log.debug('New fee level choosen:' + newFeeLevel + ' was:' + tx.feeLevel);
|
||||
|
||||
usingCustomFee = newFeeLevel == 'custom' ? true : false;
|
||||
|
||||
if (tx.feeLevel == newFeeLevel && !usingCustomFee) return;
|
||||
|
||||
tx.feeLevel = newFeeLevel;
|
||||
if (usingCustomFee) tx.feeRate = parseInt(customFeePerKB);
|
||||
|
||||
updateTx(tx, vm.originWallet, {
|
||||
clearCache: true,
|
||||
dryRun: true
|
||||
}, function() {});
|
||||
};
|
||||
};
|
||||
|
||||
function createVanityTransaction(data) {
|
||||
console.log('createVanityTransaction()');
|
||||
var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal';
|
||||
|
||||
// Grab stateParams
|
||||
tx = {
|
||||
amount: parseInt(sendFlowData.amount),
|
||||
sendMax: sendFlowData.sendMax === 'true' ? true : false,
|
||||
fromWalletId: sendFlowData.fromWalletId,
|
||||
toAddress: sendFlowData.toAddress,
|
||||
paypro: txPayproData,
|
||||
|
||||
feeLevel: configFeeLevel,
|
||||
spendUnconfirmed: config.wallet.spendUnconfirmed,
|
||||
|
||||
// Vanity tx info (not in the real tx)
|
||||
recipientType: vm.destination.kind || null,
|
||||
toName: vm.destination.name || null,
|
||||
toEmail: vm.destination.email || null,
|
||||
toColor: vm.destination.color || null,
|
||||
network: false,
|
||||
coin: vm.originWallet.coin,
|
||||
txp: {},
|
||||
};
|
||||
|
||||
|
||||
if (data.stateParams.requiredFeeRate) {
|
||||
vm.usingMerchantFee = true;
|
||||
tx.feeRate = parseInt(data.stateParams.requiredFeeRate);
|
||||
}
|
||||
|
||||
if (tx.coin && tx.coin === 'bch') {
|
||||
tx.feeLevel = 'normal';
|
||||
}
|
||||
|
||||
var B = tx.coin === 'bch' ? bitcoreCash : bitcore;
|
||||
var networkName;
|
||||
try {
|
||||
if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer
|
||||
ongoingProcess.set('generatingNewAddress', true);
|
||||
var toWallet = profileService.getWallet(destinationWalletId);
|
||||
|
||||
// We need an address to send to, so we ask the walletService to create a new address for the toWallet.
|
||||
console.log('Getting address for wallet...');
|
||||
walletService.getAddress(toWallet, true, function onWalletAddress(err, addr) {
|
||||
console.log('getAddress cb called', err);
|
||||
ongoingProcess.set('generatingNewAddress', false);
|
||||
tx.toAddress = addr;
|
||||
networkName = (new B.Address(tx.toAddress)).network.name;
|
||||
tx.network = networkName;
|
||||
console.log('calling setupTx() for wallet.');
|
||||
setupTx(tx);
|
||||
});
|
||||
} else { // This is a Wallet-to-address transfer
|
||||
networkName = (new B.Address(tx.toAddress)).network.name;
|
||||
tx.network = networkName;
|
||||
console.log('calling setupTx() for address.');
|
||||
setupTx(tx);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error setting up tx', e);
|
||||
var message = gettextCatalog.getString('Invalid address');
|
||||
popupService.showAlert(null, message, function () {
|
||||
$ionicHistory.nextViewOptions({
|
||||
disableAnimate: true,
|
||||
historyRoot: true
|
||||
});
|
||||
$state.go('tabs.send').then(function () {
|
||||
$ionicHistory.clearHistory();
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
function getOriginWalletBalance(originWallet) {
|
||||
var balanceText = getWalletBalanceDisplayText(vm.originWallet);
|
||||
vm.origin.balanceAmount = balanceText.amount;
|
||||
vm.origin.balanceCurrency = balanceText.currency;
|
||||
}
|
||||
|
||||
function getSendMaxInfo(tx, wallet, cb) {
|
||||
if (!tx.sendMax) return cb();
|
||||
|
||||
//ongoingProcess.set('retrievingInputs', true);
|
||||
walletService.getSendMaxInfo(wallet, {
|
||||
feePerKb: tx.feeRate,
|
||||
excludeUnconfirmedUtxos: !tx.spendUnconfirmed,
|
||||
returnInputs: true,
|
||||
}, cb);
|
||||
};
|
||||
|
||||
function getTxp(tx, wallet, dryRun, cb) {
|
||||
|
||||
// ToDo: use a credential's (or fc's) function for this
|
||||
if (tx.description && !wallet.credentials.sharedEncryptingKey) {
|
||||
var msg = gettextCatalog.getString('Could not add message to imported wallet without shared encrypting key');
|
||||
$log.warn(msg);
|
||||
return setSendError(msg);
|
||||
}
|
||||
|
||||
if (tx.amount > Number.MAX_SAFE_INTEGER) {
|
||||
var msg = gettextCatalog.getString('Amount too big');
|
||||
$log.warn(msg);
|
||||
return setSendError(msg);
|
||||
}
|
||||
|
||||
var txp = {};
|
||||
|
||||
txp.outputs = [{
|
||||
'toAddress': tx.toAddress,
|
||||
'amount': tx.amount,
|
||||
'message': vm.memo
|
||||
}];
|
||||
|
||||
if (tx.sendMaxInfo) {
|
||||
txp.inputs = tx.sendMaxInfo.inputs;
|
||||
txp.fee = tx.sendMaxInfo.fee;
|
||||
} else {
|
||||
if (usingCustomFee || usingMerchantFee) {
|
||||
txp.feePerKb = tx.feeRate;
|
||||
} else txp.feeLevel = tx.feeLevel;
|
||||
}
|
||||
|
||||
txp.message = vm.memo;
|
||||
|
||||
if (tx.paypro) {
|
||||
txp.payProUrl = tx.paypro.url;
|
||||
}
|
||||
txp.excludeUnconfirmedUtxos = !tx.spendUnconfirmed;
|
||||
txp.dryRun = dryRun;
|
||||
walletService.createTx(wallet, txp, function(err, ctxp) {
|
||||
if (err) {
|
||||
setSendError(err);
|
||||
return cb(err);
|
||||
}
|
||||
return cb(null, ctxp);
|
||||
});
|
||||
};
|
||||
|
||||
function getWalletBalanceDisplayText(wallet) {
|
||||
var balanceCryptoAmount = '';
|
||||
var balanceCryptoCurrencyCode = '';
|
||||
var balanceFiatAmount = '';
|
||||
var balanceFiatCurrency = ''
|
||||
var displayAmount = '';
|
||||
var displayCurrency = '';
|
||||
|
||||
var walletStatus = null;
|
||||
if (wallet.status.isValid) {
|
||||
walletStatus = wallet.status;
|
||||
} else if (wallet.cachedStatus.isValid) {
|
||||
walletStatus = wallet.cachedStatus;
|
||||
}
|
||||
|
||||
if (walletStatus) {
|
||||
var cryptoBalanceParts = walletStatus.spendableBalanceStr.split(' ');
|
||||
balanceCryptoAmount = cryptoBalanceParts[0];
|
||||
balanceCryptoCurrencyCode = cryptoBalanceParts.length > 1 ? cryptoBalanceParts[1] : '';
|
||||
|
||||
if (walletStatus.alternativeBalanceAvailable) {
|
||||
balanceFiatAmount = walletStatus.spendableBalanceAlternative;
|
||||
balanceFiatCurrency = walletStatus.alternativeIsoCode;
|
||||
}
|
||||
}
|
||||
|
||||
if (priceDisplayIsFiat) {
|
||||
displayAmount = balanceFiatAmount ? balanceFiatAmount : balanceCryptoAmount;
|
||||
displayCurrency = balanceFiatAmount ? balanceFiatCurrency : balanceCryptoCurrencyCode;
|
||||
} else {
|
||||
displayAmount = balanceCryptoAmount;
|
||||
displayCurrency = balanceCryptoCurrencyCode;
|
||||
}
|
||||
|
||||
return {
|
||||
amount: displayAmount,
|
||||
currency: displayCurrency
|
||||
};
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
$ionicHistory.goBack();
|
||||
}
|
||||
|
||||
function handleDestinationAsAddress(address, originCoin) {
|
||||
if (!address) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the recipient is a contact
|
||||
addressbookService.get(originCoin + address, function(err, contact) {
|
||||
if (!err && contact) {
|
||||
handleDestinationAsAddressOfContact(contact);
|
||||
} else {
|
||||
if (originCoin === 'bch') {
|
||||
vm.destination.address = bitcoinCashJsService.readAddress(address).cashaddr;
|
||||
} else {
|
||||
vm.destination.address = address;
|
||||
}
|
||||
vm.destination.kind = 'address';
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function handleDestinationAsAddressOfContact(contact) {
|
||||
vm.destination.kind = 'contact';
|
||||
vm.destination.name = contact.name;
|
||||
vm.destination.email = contact.email;
|
||||
vm.destination.color = contact.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor;
|
||||
vm.destination.currency = contact.coin.toUpperCase();
|
||||
vm.destination.currencyColor = vm.destination.color;
|
||||
}
|
||||
|
||||
function handleDestinationAsWallet(walletId) {
|
||||
destinationWalletId = walletId;
|
||||
if (!destinationWalletId) {
|
||||
return;
|
||||
}
|
||||
|
||||
var destinationWallet = profileService.getWallet(destinationWalletId);
|
||||
vm.destination.coin = destinationWallet.coin;
|
||||
vm.destination.color = destinationWallet.color;
|
||||
vm.destination.currency = destinationWallet.coin.toUpperCase();
|
||||
vm.destination.kind = 'wallet';
|
||||
vm.destination.name = destinationWallet.name;
|
||||
|
||||
if (defaults) {
|
||||
vm.destination.currencyColor = vm.destination.coin === 'btc' ? defaults.bitcoinWalletColor : defaults.bitcoinCashWalletColor;
|
||||
}
|
||||
|
||||
var balanceText = getWalletBalanceDisplayText(destinationWallet);
|
||||
vm.destination.balanceAmount = balanceText.amount;
|
||||
vm.destination.balanceCurrency = balanceText.currency;
|
||||
}
|
||||
|
||||
function handleThirdPartyInitIfBip70() {
|
||||
if (vm.thirdParty.id === 'bip70') {
|
||||
vm.sendingTitle = gettextCatalog.getString('You are paying');
|
||||
vm.memo = vm.thirdParty.memo;
|
||||
vm.memoExpanded = !!vm.memo;
|
||||
vm.destination.name = vm.thirdParty.name;
|
||||
|
||||
txPayproData = {
|
||||
caTrusted: vm.thirdParty.caTrusted,
|
||||
domain: vm.thirdParty.domain,
|
||||
expires: vm.thirdParty.expires,
|
||||
toAddress: toAddress,
|
||||
url: vm.thirdParty.url,
|
||||
verified: vm.thirdParty.verified,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function handleThirdPartyInitIfShapeshift() {
|
||||
if (vm.thirdParty.id === 'shapeshift') {
|
||||
vm.sendingTitle = gettextCatalog.getString('You are shifting');
|
||||
if (!vm.thirdParty.data) {
|
||||
vm.thirdParty.data = {};
|
||||
}
|
||||
|
||||
var toWallet = profileService.getWallet(destinationWalletId);
|
||||
vm.destination.name = toWallet.name;
|
||||
vm.destination.color = toWallet.color;
|
||||
vm.destination.currency = toWallet.coin.toUpperCase();
|
||||
|
||||
|
||||
ongoingProcess.set('connectingShapeshift', true);
|
||||
walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) {
|
||||
if (err) {
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString());
|
||||
return;
|
||||
}
|
||||
walletService.getAddress(toWallet, false, function onWithdrawalWalletAddress(err, withdrawalAddr) {
|
||||
if (err) {
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
shapeshiftService.shiftIt(vm.originWallet.coin, toWallet.coin, withdrawalAddr, returnAddr, function onShiftIt(err, shapeshiftData) {
|
||||
if (err && err != null) {
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString());
|
||||
} else {
|
||||
vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId;
|
||||
vm.memoExpanded = !!vm.memo;
|
||||
tx.toAddress = shapeshiftData.toAddress;
|
||||
vm.destination.address = toAddress;
|
||||
vm.destination.kind = 'shapeshift';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function startExpirationTimer(expirationTime) {
|
||||
vm.paymentExpired = false;
|
||||
setExpirationTime();
|
||||
|
||||
countDown = $interval(function() {
|
||||
setExpirationTime();
|
||||
}, 1000);
|
||||
|
||||
function setExpirationTime() {
|
||||
console.log('setExpirationTime()');
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
|
||||
if (now > expirationTime) {
|
||||
setExpiredValues();
|
||||
return;
|
||||
}
|
||||
|
||||
var totalSecs = expirationTime - now;
|
||||
var m = Math.floor(totalSecs / 60);
|
||||
var s = totalSecs % 60;
|
||||
vm.remainingTimeStr = m + ":" + ('0' + s).slice(-2);
|
||||
};
|
||||
|
||||
function setExpiredValues() {
|
||||
vm.paymentExpired = true;
|
||||
vm.remainingTimeStr = gettextCatalog.getString('Expired');
|
||||
vm.readyToSend = false;
|
||||
if (countDown) $interval.cancel(countDown);
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function updateSendAmounts() {
|
||||
if (typeof satoshis !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
var cryptoAmount = '';
|
||||
var cryptoCurrencyCode = '';
|
||||
var amountStr = txFormatService.formatAmountStr(coin, satoshis);
|
||||
if (amountStr) {
|
||||
var amountParts = amountStr.split(' ');
|
||||
cryptoAmount = amountParts[0];
|
||||
cryptoCurrencyCode = amountParts.length > 1 ? amountParts[1] : '';
|
||||
}
|
||||
// Want to avoid flashing of amount strings so do all formatting after this has returned.
|
||||
txFormatService.formatAlternativeStr(coin, satoshis, function(v) {
|
||||
if (!v) {
|
||||
vm.primaryAmount = cryptoAmount;
|
||||
vm.primaryCurrency = cryptoCurrencyCode;
|
||||
vm.secondaryAmount = '';
|
||||
vm.secondaryCurrency = '';
|
||||
return;
|
||||
}
|
||||
vm.secondaryAmount = vm.primaryAmount;
|
||||
vm.secondaryCurrency = vm.primaryCurrency;
|
||||
|
||||
var fiatParts = v.split(' ');
|
||||
var fiatAmount = fiatParts[0];
|
||||
var fiatCurrency = fiatParts.length > 1 ? fiatParts[1] : '';
|
||||
|
||||
if (priceDisplayIsFiat) {
|
||||
vm.primaryAmount = fiatAmount;
|
||||
vm.primaryCurrency = fiatCurrency;
|
||||
vm.secondaryAmount = cryptoAmount;
|
||||
vm.secondaryCurrency = cryptoCurrencyCode;
|
||||
} else {
|
||||
vm.primaryAmount = cryptoAmount;
|
||||
vm.primaryCurrency = cryptoCurrencyCode;
|
||||
vm.secondaryAmount = fiatAmount;
|
||||
vm.secondaryCurrency = fiatCurrency;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onSuccessConfirm() {
|
||||
vm.sendStatus = '';
|
||||
$ionicHistory.nextViewOptions({
|
||||
disableAnimate: true,
|
||||
historyRoot: true
|
||||
});
|
||||
$state.go('tabs.send').then(function() {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.transitionTo('tabs.home');
|
||||
});
|
||||
};
|
||||
|
||||
function setButtonText(isMultisig, isPayPro) {
|
||||
if (isPayPro) {
|
||||
if (vm.isCordova) {
|
||||
vm.buttonText = gettextCatalog.getString('Slide to pay');
|
||||
} else {
|
||||
vm.buttonText = gettextCatalog.getString('Click to pay');
|
||||
}
|
||||
} else if (isMultisig) {
|
||||
if (vm.isCordova) {
|
||||
vm.buttonText = gettextCatalog.getString('Slide to accept');
|
||||
} else {
|
||||
vm.buttonText = gettextCatalog.getString('Click to accept');
|
||||
}
|
||||
} else {
|
||||
if (vm.isCordova) {
|
||||
vm.buttonText = gettextCatalog.getString('Slide to send');
|
||||
} else {
|
||||
vm.buttonText = gettextCatalog.getString('Click to send');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setNotReady(msg, criticalError) {
|
||||
vn.readyToSend = false;
|
||||
vm.notReadyMessage = msg;
|
||||
$scope.criticalError = criticalError;
|
||||
$log.warn('Not ready to make the payment:' + msg);
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
function setSendError(msg) {
|
||||
$scope.sendStatus = '';
|
||||
vm.readyToSend = false;
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg));
|
||||
};
|
||||
|
||||
function setupTx(tx) {
|
||||
if (tx.coin === 'bch') {
|
||||
tx.displayAddress = bitcoinCashJsService.readAddress(tx.toAddress).cashaddr;
|
||||
} else {
|
||||
tx.displayAddress = tx.toAddress;
|
||||
}
|
||||
|
||||
addressbookService.get(tx.coin+tx.toAddress, function(err, addr) { // Check if the recipient is a contact
|
||||
if (!err && addr) {
|
||||
tx.toName = addr.name;
|
||||
tx.toEmail = addr.email;
|
||||
tx.recipientType = 'contact';
|
||||
}
|
||||
});
|
||||
|
||||
vm.showAddress = false;
|
||||
|
||||
|
||||
setButtonText(vm.originWallet.credentials.m > 1, !!tx.paypro);
|
||||
|
||||
if (tx.paypro)
|
||||
startExpirationTimer(tx.paypro.expires);
|
||||
|
||||
updateTx(tx, vm.originWallet, {
|
||||
dryRun: true
|
||||
}, function(err) {
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
}, 10);
|
||||
|
||||
});
|
||||
|
||||
// setWalletSelector(tx.coin, tx.network, tx.amount, function(err) {
|
||||
// if (err) {
|
||||
// return exitWithError('Could not update wallets');
|
||||
// }
|
||||
//
|
||||
// if (vm.wallets.length > 1) {
|
||||
// vm.showWalletSelector();
|
||||
// } else if (vm.wallets.length) {
|
||||
// setWallet(vm.wallets[0], tx);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
function showSendMaxWarning(wallet, sendMaxInfo) {
|
||||
var feeAlternative = '',
|
||||
msg = '';
|
||||
|
||||
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(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(vm.originWallet.coin, sendMaxInfo.amountAboveMaxSize)
|
||||
}));
|
||||
}
|
||||
return warningMsg.join('\n');
|
||||
};
|
||||
|
||||
feeAlternative = txFormatService.formatAlternativeStr(vm.originWallet.coin, sendMaxInfo.fee);
|
||||
if (feeAlternative) {
|
||||
msg = gettextCatalog.getString("{{feeAlternative}} will be deducted for bitcoin networking fees ({{fee}}).", {
|
||||
fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee),
|
||||
feeAlternative: feeAlternative
|
||||
});
|
||||
} else {
|
||||
msg = gettextCatalog.getString("{{fee}} will be deducted for bitcoin networking fees).", {
|
||||
fee: txFormatService.formatAmountStr(vm.originWallet.coin, sendMaxInfo.fee)
|
||||
});
|
||||
}
|
||||
|
||||
var warningMsg = verifyExcludedUtxos();
|
||||
|
||||
if (!lodash.isEmpty(warningMsg))
|
||||
msg += '\n' + warningMsg;
|
||||
|
||||
popupService.showAlert(null, msg, function() {});
|
||||
};
|
||||
|
||||
function statusChangeHandler(processName, showName, isOn) {
|
||||
$log.debug('statusChangeHandler: ', processName, showName, isOn);
|
||||
if (
|
||||
(
|
||||
processName === 'broadcastingTx' ||
|
||||
((processName === 'signingTx') && vm.originWallet.m > 1) ||
|
||||
(processName == 'sendingTx' && !vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal())
|
||||
) && !isOn) {
|
||||
vm.sendStatus = 'success';
|
||||
|
||||
if ($state.current.name === "tabs.send.review") { // XX SP: Otherwise all open wallets on other devices play this sound if you have been in a send flow before on that device.
|
||||
soundService.play('misc/payment_sent.mp3');
|
||||
}
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
}
|
||||
// When displaying Fiat, if the formatting fails, the crypto will be the primary amount.
|
||||
var amount = unitFromSat * satoshis;
|
||||
var log = new window.BitAnalytics.LogEvent("transfer_success", [{
|
||||
"coin": vm.originWallet.coin,
|
||||
"type": "outgoing",
|
||||
"amount": amount,
|
||||
"fees": vm.feeCrypto
|
||||
}], [channel, "adjust"]);
|
||||
window.BitAnalytics.LogEventHandlers.postEvent(log);
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 100);
|
||||
} else if (showName) {
|
||||
vm.sendStatus = showName;
|
||||
}
|
||||
};
|
||||
|
||||
function updateTx(tx, wallet, opts, cb) {
|
||||
ongoingProcess.set('calculatingFee', true);
|
||||
|
||||
if (opts.clearCache) {
|
||||
tx.txp = {};
|
||||
}
|
||||
|
||||
// $scope.tx = tx;
|
||||
|
||||
// function updateAmount() {
|
||||
// if (!tx.amount) return;
|
||||
//
|
||||
// // Amount
|
||||
// tx.amountStr = txFormatService.formatAmountStr(originWallet.coin, tx.amount);
|
||||
// tx.amountValueStr = tx.amountStr.split(' ')[0];
|
||||
// tx.amountUnitStr = tx.amountStr.split(' ')[1];
|
||||
// txFormatService.formatAlternativeStr(wallet.coin, tx.amount, function(v) {
|
||||
// var parts = v.split(' ');
|
||||
// tx.alternativeAmountStr = v;
|
||||
// tx.alternativeAmountValueStr = parts[0];
|
||||
// tx.alternativeAmountUnitStr = (parts.length > 0) ? parts[1] : '';
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// updateAmount();
|
||||
// refresh();
|
||||
|
||||
var feeServiceLevel = usingMerchantFee && vm.originWallet.coin == 'btc' ? 'urgent' : tx.feeLevel;
|
||||
feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function(err, feeRate) {
|
||||
if (err) {
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
var msg;
|
||||
if (usingCustomFee) {
|
||||
msg = gettextCatalog.getString('Custom');
|
||||
tx.feeLevelName = msg;
|
||||
} else if (usingMerchantFee) {
|
||||
$log.info('Using Merchant Fee:' + tx.feeRate + ' vs. Urgent level:' + feeRate);
|
||||
msg = gettextCatalog.getString('Suggested by Merchant');
|
||||
tx.feeLevelName = msg;
|
||||
} else {
|
||||
tx.feeLevelName = feeService.feeOpts[tx.feeLevel];
|
||||
tx.feeRate = feeRate;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (sendMaxInfo) {
|
||||
|
||||
$log.debug('Send max info', sendMaxInfo);
|
||||
|
||||
if (tx.sendMax && sendMaxInfo.amount == 0) {
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
setNotReady(gettextCatalog.getString('Insufficient confirmed funds'));
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'));
|
||||
return cb('no_funds');
|
||||
}
|
||||
|
||||
tx.sendMaxInfo = sendMaxInfo;
|
||||
tx.amount = tx.sendMaxInfo.amount;
|
||||
satoshis = tx.amount;
|
||||
updateSendAmounts();
|
||||
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);
|
||||
vm.readyToSend = true;
|
||||
updateSendAmounts();
|
||||
$scope.$apply();
|
||||
return cb();
|
||||
}
|
||||
|
||||
console.log('calling getTxp() from getSendMaxInfo cb.');
|
||||
getTxp(lodash.clone(tx), wallet, opts.dryRun, function(err, txp) {
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
if (err) {
|
||||
if (err.message == 'Insufficient funds') {
|
||||
setNotReady(gettextCatalog.getString('Insufficient funds'));
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'));
|
||||
return cb('no_funds');
|
||||
} else
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee);
|
||||
txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) {
|
||||
// txp.alternativeFeeStr = v;
|
||||
// if (txp.alternativeFeeStr.substring(0, 4) == '0.00')
|
||||
// txp.alternativeFeeStr = '< ' + txp.alternativeFeeStr;
|
||||
vm.feeFiat = v;
|
||||
vm.fiatCurrency = config.wallet.settings.alternativeIsoCode;
|
||||
if (v.substring(0, 1) === "<") {
|
||||
vm.feeLessThanACent = true;
|
||||
}
|
||||
|
||||
console.log("fiat", vm.feeFiat);
|
||||
|
||||
});
|
||||
|
||||
var per = (txp.fee / (txp.amount + txp.fee) * 100);
|
||||
var perString = per.toFixed(2);
|
||||
txp.feeRatePerStr = (perString == '0.00' ? '< ' : '') + perString + '%';
|
||||
txp.feeToHigh = per > FEE_TOO_HIGH_LIMIT_PERCENTAGE;
|
||||
vm.feeCrypto = (unitFromSat * txp.fee).toFixed(8);
|
||||
vm.feeIsHigh = txp.feeToHigh;
|
||||
console.log("crypto", vm.feeCrypto);
|
||||
|
||||
|
||||
tx.txp[wallet.id] = txp;
|
||||
$log.debug('Confirm. TX Fully Updated for wallet:' + wallet.id, tx);
|
||||
vm.readyToSend = true;
|
||||
updateSendAmounts();
|
||||
console.log('readyToSend:', vm.readyToSend);
|
||||
$scope.$apply();
|
||||
|
||||
return cb();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('shapeshiftController', function($scope, $interval, profileService, walletService, popupService, lodash, $ionicNavBarDelegate) {
|
||||
|
||||
angular.module('copayApp.controllers').controller('shapeshiftController', function($scope, sendFlowService, $state, $timeout, $ionicHistory, profileService, walletService, popupService, lodash, $ionicNavBarDelegate) {
|
||||
var walletsBtc = [];
|
||||
var walletsBch = [];
|
||||
|
||||
|
|
@ -16,24 +15,9 @@ angular.module('copayApp.controllers').controller('shapeshiftController', functi
|
|||
}
|
||||
|
||||
function showToWallets() {
|
||||
$scope.toWallets = $scope.fromWallet.coin == 'btc' ? walletsBch : walletsBtc;
|
||||
$scope.toWallets = $scope.fromWallet.coin === 'btc' ? walletsBch : walletsBtc;
|
||||
$scope.onToWalletSelect($scope.toWallets[0]);
|
||||
$scope.singleToWallet = $scope.toWallets.length == 1;
|
||||
}
|
||||
|
||||
$scope.onFromWalletSelect = function(wallet) {
|
||||
$scope.fromWallet = wallet;
|
||||
showToWallets();
|
||||
generateAddress(wallet, function(addr) {
|
||||
$scope.fromWalletAddress = addr;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.onToWalletSelect = function(wallet) {
|
||||
$scope.toWallet = wallet;
|
||||
generateAddress(wallet, function(addr) {
|
||||
$scope.toWalletAddress = addr;
|
||||
});
|
||||
$scope.singleToWallet = $scope.toWallets.length === 1;
|
||||
}
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
|
|
@ -42,15 +26,15 @@ angular.module('copayApp.controllers').controller('shapeshiftController', functi
|
|||
$scope.fromWallets = lodash.filter(walletsBtc.concat(walletsBch), function(w) {
|
||||
return w.status.balance.availableAmount > 0;
|
||||
});
|
||||
if ($scope.fromWallets.length == 0) return;
|
||||
$scope.onFromWalletSelect($scope.fromWallets[0]);
|
||||
$scope.onToWalletSelect($scope.toWallets[0]);
|
||||
$scope.singleFromWallet = $scope.fromWallets.length == 1;
|
||||
$scope.singleToWallet = $scope.toWallets.length == 1;
|
||||
|
||||
$scope.singleFromWallet = $scope.fromWallets.length === 1;
|
||||
$scope.fromWalletSelectorTitle = 'From';
|
||||
$scope.toWalletSelectorTitle = 'To';
|
||||
$scope.showFromWallets = false;
|
||||
$scope.showToWallets = false;
|
||||
$scope.walletsWithFunds = profileService.getWallets({onlyComplete: true, hasFunds: true});
|
||||
$scope.wallets = profileService.getWallets({onlyComplete: true});
|
||||
$scope.hasWallets = !lodash.isEmpty($scope.wallets);
|
||||
});
|
||||
|
||||
$scope.$on("$ionicView.enter", function(event, data) {
|
||||
|
|
@ -59,9 +43,42 @@ angular.module('copayApp.controllers').controller('shapeshiftController', functi
|
|||
|
||||
$scope.showFromWalletSelector = function() {
|
||||
$scope.showFromWallets = true;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showToWalletSelector = function() {
|
||||
$scope.showToWallets = true;
|
||||
};
|
||||
|
||||
// This could probably be enhanced refactoring the routes abstract states
|
||||
$scope.createWallet = function() {
|
||||
$state.go('tabs.home').then(function() {
|
||||
$state.go('tabs.add.create-personal');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.buyBitcoin = function() {
|
||||
$state.go('tabs.home').then(function() {
|
||||
$state.go('tabs.buyandsell');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.shapeshift = function() {
|
||||
var stateParams = {
|
||||
thirdParty: {
|
||||
id: 'shapeshift'
|
||||
}
|
||||
};
|
||||
|
||||
// Starting new send flow, so ensure everything is reset
|
||||
sendFlowService.clear();
|
||||
$state.go('tabs.home').then(function() {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.send').then(function() {
|
||||
$timeout(function () {
|
||||
sendFlowService.pushState(stateParams);
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
}, 60);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabHomeController',
|
||||
function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, bannerService, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService, timeService, bitcoincomService, pricechartService, firebaseEventsService, servicesService, shapeshiftService, $ionicNavBarDelegate, signVerifyMessageService) {
|
||||
function($rootScope, sendFlowService, $timeout, $scope, $state, $stateParams, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, bannerService, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, storageService, txpModalService, appConfigService, startupService, addressbookService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService, timeService, $ionicNavBarDelegate) {
|
||||
var wallet;
|
||||
var listeners = [];
|
||||
var notifications = [];
|
||||
|
|
@ -20,7 +20,12 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
$scope.bannerUrl = '';
|
||||
|
||||
|
||||
$scope.$on("$ionicView.afterEnter", function() {
|
||||
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
|
||||
$scope.$on("$ionicView.enter", onEnter);
|
||||
$scope.$on("$ionicView.afterEnter", onAfterEnter);
|
||||
$scope.$on("$ionicView.leave", onLeave);
|
||||
|
||||
function onAfterEnter () {
|
||||
startupService.ready();
|
||||
|
||||
bannerService.getBanner(function (banner) {
|
||||
|
|
@ -28,9 +33,10 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
$scope.bannerUrl = banner.url;
|
||||
$scope.bannerIsLoading = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function onBeforeEnter (event, data) {
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
if (!$scope.homeTip) {
|
||||
storageService.getHomeTipAccepted(function(error, value) {
|
||||
$scope.homeTip = (value == 'accepted') ? false : true;
|
||||
|
|
@ -51,9 +57,9 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on("$ionicView.enter", function(event, data) {
|
||||
};
|
||||
|
||||
function onEnter(event, data) {
|
||||
$ionicNavBarDelegate.showBar(true);
|
||||
updateAllWallets();
|
||||
|
||||
|
|
@ -97,26 +103,29 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
}
|
||||
|
||||
$scope.showServices = true;
|
||||
pushNotificationsService.init();
|
||||
firebaseEventsService.init();
|
||||
|
||||
$timeout(function() {
|
||||
$ionicScrollDelegate.resize();
|
||||
$scope.$apply();
|
||||
}, 10);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.leave", function(event, data) {
|
||||
function onLeave (event, data) {
|
||||
lodash.each(listeners, function(x) {
|
||||
x();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.createdWithinPastDay = function(time) {
|
||||
return timeService.withinPastDay(time);
|
||||
};
|
||||
|
||||
$scope.startFreshSend = function() {
|
||||
sendFlowService.clear();
|
||||
$state.go('tabs.send');
|
||||
}
|
||||
|
||||
$scope.openExternalLink = function() {
|
||||
var url = 'https://github.com/Bitcoin-com/Wallet/releases/latest';
|
||||
var optIn = true;
|
||||
|
|
@ -224,6 +233,7 @@ angular.module('copayApp.controllers').controller('tabHomeController',
|
|||
cb();
|
||||
}
|
||||
}
|
||||
$scope.walletsWithFunds = profileService.getWallets({hasFunds: true});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
|
|||
|
||||
$scope.requestSpecificAmount = function() {
|
||||
$state.go('tabs.paymentRequest.amount', {
|
||||
id: $scope.wallet.credentials.walletId,
|
||||
coin: $scope.wallet.coin
|
||||
toWalletId: $scope.wallet.credentials.walletId
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -145,9 +144,9 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
|
|||
}
|
||||
$scope.paymentReceivedCoin = $scope.wallet.coin;
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("transfer_success", [{
|
||||
"coin": $scope.wallet.coin,
|
||||
|
|
@ -233,10 +232,14 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi
|
|||
|
||||
if (!$scope.wallets[0]) return;
|
||||
|
||||
// select first wallet if no wallet selected previously
|
||||
var selectedWallet = checkSelectedWallet($scope.wallet, $scope.wallets);
|
||||
var selectedWallet = null;
|
||||
if (data.stateParams.walletId) { // from walletDetails
|
||||
selectedWallet = checkSelectedWallet(profileService.getWallet(data.stateParams.walletId), $scope.wallets);
|
||||
} else {
|
||||
// select first wallet if no wallet selected previously
|
||||
selectedWallet = checkSelectedWallet($scope.wallet, $scope.wallets);
|
||||
}
|
||||
$scope.onWalletSelect(selectedWallet);
|
||||
|
||||
$scope.showShareButton = platformInfo.isCordova ? (platformInfo.isIOS ? 'iOS' : 'Android') : null;
|
||||
|
||||
listeners = [
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
|
||||
angular.module('copayApp.controllers').controller('tabSendController', function($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicLoading, addressbookService, profileService, lodash, $state, walletService, incomingData, popupService, platformInfo, sendFlowService, bwcError, gettextCatalog, scannerService, configService, bitcoinCashJsService, $ionicPopup, $ionicNavBarDelegate, clipboardService) {
|
||||
var clipboardHasAddress = false;
|
||||
var clipboardHasContent = false;
|
||||
var originalList;
|
||||
|
|
@ -33,6 +33,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
text = text.substring(0, 200);
|
||||
}
|
||||
|
||||
var stateParams = sendFlowService.getState();
|
||||
$scope.fromWallet = profileService.getWallet(stateParams.fromWalletId);
|
||||
|
||||
$scope.clipboardHasAddress = false;
|
||||
$scope.clipboardHasContent = false;
|
||||
if ((text.indexOf('bitcoincash:') === 0 || text[0] === 'C' || text[0] === 'H' || text[0] === 'p' || text[0] === 'q') && text.replace('bitcoincash:', '').length === 42) { // CashAddr
|
||||
|
|
@ -55,42 +58,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
});
|
||||
});
|
||||
|
||||
var wallets;
|
||||
var walletsBch;
|
||||
var walletsBtc;
|
||||
var walletToWalletFrom = false;
|
||||
|
||||
$scope.onWalletSelect = function(wallet) {
|
||||
if (!$scope.walletToWalletFrom) {
|
||||
$scope.walletToWalletFrom = wallet;
|
||||
if (wallet.coin === 'bch') {
|
||||
$scope.showWalletsBch = true;
|
||||
} else if (wallet.coin === 'btc') {
|
||||
$scope.showWalletsBtc = true;
|
||||
}
|
||||
$scope.walletSelectorTitleTo = gettextCatalog.getString('Send to');
|
||||
} else {
|
||||
$ionicLoading.show();
|
||||
walletService.getAddress(wallet, true, function(err, addr) {
|
||||
$ionicLoading.hide();
|
||||
return $state.transitionTo('tabs.send.amount', {
|
||||
displayAddress: $scope.walletToWalletFrom.coin === 'bch' ? bitcoinCashJsService.translateAddresses(addr).cashaddr : addr,
|
||||
recipientType: 'wallet',
|
||||
fromWalletId: $scope.walletToWalletFrom.id,
|
||||
toAddress: addr,
|
||||
coin: $scope.walletToWalletFrom.coin
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showWalletSelector = function() {
|
||||
$scope.walletToWalletFrom = false;
|
||||
$scope.walletSelectorTitleFrom = gettextCatalog.getString('Send from');
|
||||
$scope.showWallets = true;
|
||||
};
|
||||
|
||||
$scope.findContact = function(search) {
|
||||
|
||||
if (incomingData.redir(search)) {
|
||||
|
|
@ -133,7 +100,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
};
|
||||
|
||||
var updateHasFunds = function() {
|
||||
|
||||
$scope.hasFunds = false;
|
||||
var index = 0;
|
||||
lodash.each($scope.wallets, function(w) {
|
||||
|
|
@ -179,10 +145,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
coin: v.coin,
|
||||
displayCoin: (v.coin == 'bch'
|
||||
? (config.bitcoinCashAlias || defaults.bitcoinCashAlias)
|
||||
: (config.bitcoinAlias || defaults.bitcoinAlias)).toUpperCase(),
|
||||
getAddress: function(cb) {
|
||||
return cb(null, k);
|
||||
},
|
||||
: (config.bitcoinAlias || defaults.bitcoinAlias)).toUpperCase()
|
||||
});
|
||||
});
|
||||
originalList = completeContacts;
|
||||
|
|
@ -203,39 +166,46 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
};
|
||||
|
||||
$scope.searchBlurred = function() {
|
||||
if ($scope.formData.search == null || $scope.formData.search.length == 0) {
|
||||
if ($scope.formData.search == null || $scope.formData.search.length === 0) {
|
||||
$scope.searchFocus = false;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.goToAmount = function(item) {
|
||||
$timeout(function() {
|
||||
item.getAddress(function(err, addr) {
|
||||
if (err || !addr) {
|
||||
//Error is already formated
|
||||
return popupService.showAlert(err);
|
||||
}
|
||||
$scope.sendToContact = function (item) {
|
||||
$timeout(function () {
|
||||
var toAddress = item.address;
|
||||
|
||||
if (item.recipientType && item.recipientType == 'contact') {
|
||||
if (addr.indexOf('bch') == 0 || addr.indexOf('btc') == 0) {
|
||||
addr = addr.substring(3);
|
||||
}
|
||||
if (item.recipientType && item.recipientType === 'contact') {
|
||||
if (toAddress.indexOf('bch') === 0 || toAddress.indexOf('btc') === 0) {
|
||||
toAddress = toAddress.substring(3);
|
||||
}
|
||||
}
|
||||
|
||||
$log.debug('Got toAddress:' + toAddress + ' | ' + item.name);
|
||||
|
||||
var stateParams = sendFlowService.getState();
|
||||
stateParams.toAddress = toAddress,
|
||||
stateParams.coin = item.coin;
|
||||
sendFlowService.pushState(stateParams);
|
||||
|
||||
if (!stateParams.fromWalletId) { // If we have no toAddress or fromWallet
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
} else {
|
||||
$state.transitionTo('tabs.send.amount');
|
||||
}
|
||||
|
||||
$log.debug('Got toAddress:' + addr + ' | ' + item.name);
|
||||
return $state.transitionTo('tabs.send.amount', {
|
||||
recipientType: item.recipientType,
|
||||
displayAddress: item.coin == 'bch' ? bitcoinCashJsService.translateAddresses(addr).cashaddr : addr,
|
||||
toAddress: addr,
|
||||
toName: item.name,
|
||||
toEmail: item.email,
|
||||
toColor: item.color,
|
||||
coin: item.coin
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.startWalletToWalletTransfer = function() {
|
||||
console.log('startWalletToWalletTransfer()');
|
||||
var params = sendFlowService.getState();
|
||||
sendFlowService.pushState(params);
|
||||
$state.transitionTo('tabs.send.wallet-to-wallet', {
|
||||
fromWalletId: sendFlowService.fromWalletId
|
||||
});
|
||||
}
|
||||
|
||||
// This could probably be enhanced refactoring the routes abstract states
|
||||
$scope.createWallet = function() {
|
||||
$state.go('tabs.home').then(function() {
|
||||
|
|
@ -250,6 +220,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
console.log(data);
|
||||
console.log('tab-send onBeforeEnter sendflow ', sendFlowService.getState());
|
||||
$scope.isIOS = platformInfo.isIOS && platformInfo.isCordova;
|
||||
$scope.showWalletsBch = $scope.showWalletsBtc = $scope.showWallets = false;
|
||||
|
||||
|
|
@ -264,5 +236,9 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
$scope.displayBalanceAsFiat = _config.wallet.settings.priceDisplay === 'fiat';
|
||||
});
|
||||
|
||||
if (data.direction == "back") {
|
||||
sendFlowService.clear();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -16,7 +16,7 @@ angular.module('copayApp.controllers').controller('tabSettingsController', funct
|
|||
isoCode: config.wallet.settings.alternativeIsoCode
|
||||
};
|
||||
|
||||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
|
||||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay === 'crypto' ? gettextCatalog.getString('Cryptocurrency') : gettextCatalog.getString('Fiat');
|
||||
|
||||
// TODO move this to a generic service
|
||||
bitpayAccountService.getAccounts(function(err, data) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, platformInfo, incomingData, lodash, popupService, gettextCatalog, scannerService) {
|
||||
angular.module('copayApp.controllers').controller('tabsController', function($rootScope, $log, $scope, $state, $stateParams, $timeout, platformInfo, incomingData, lodash, popupService, gettextCatalog, scannerService, sendFlowService) {
|
||||
|
||||
$scope.onScan = function(data) {
|
||||
if (!incomingData.redir(data)) {
|
||||
|
|
@ -15,6 +15,11 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
|
|||
};
|
||||
};
|
||||
|
||||
$scope.startFreshSend = function() {
|
||||
sendFlowService.clear();
|
||||
$state.go('tabs.send');
|
||||
};
|
||||
|
||||
$scope.importInit = function() {
|
||||
$scope.fromOnboarding = $stateParams.fromOnboarding;
|
||||
$timeout(function() {
|
||||
|
|
@ -23,7 +28,7 @@ angular.module('copayApp.controllers').controller('tabsController', function($ro
|
|||
};
|
||||
|
||||
$scope.chooseScanner = function() {
|
||||
|
||||
sendFlowService.clear();
|
||||
var isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
|
||||
|
||||
if (!isWindowsPhoneApp) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService, feeService, appConfigService, rateService) {
|
||||
angular.module('copayApp.controllers').controller('walletDetailsController', function($scope, $rootScope, $interval, $timeout, $filter, $log, $ionicModal, $ionicPopover, $state, $stateParams, $ionicHistory, profileService, lodash, configService, platformInfo, walletService, txpModalService, externalLinkService, popupService, addressbookService, sendFlowService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService, feeService, appConfigService, rateService) {
|
||||
|
||||
var HISTORY_SHOW_LIMIT = 10;
|
||||
var currentTxHistoryPage = 0;
|
||||
|
|
@ -12,9 +12,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
|
|||
$scope.isAndroid = platformInfo.isAndroid;
|
||||
$scope.isIOS = platformInfo.isIOS;
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("wallet_details_open", [], [channel]);
|
||||
window.BitAnalytics.LogEventHandlers.postEvent(log);
|
||||
|
|
@ -342,9 +342,9 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
|
|||
top = TOP_BALANCE_BUTTON;
|
||||
}
|
||||
|
||||
var amountTop = ((amountScale - 0.7) / 0.7) * top;
|
||||
if (amountTop < -10) {
|
||||
amountTop = -10;
|
||||
var amountTop = ((amountScale - 0.80) / 0.80) * top;
|
||||
if (amountTop < -2) {
|
||||
amountTop = -2;
|
||||
}
|
||||
if (amountTop > top) {
|
||||
amountTop = top;
|
||||
|
|
@ -353,6 +353,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
|
|||
var t = amountTop;
|
||||
|
||||
$scope.altAmountOpacity = (amountHeight - 100) / 80;
|
||||
$scope.buttonsOpacity = (amountHeight - 140) / 70;
|
||||
$window.requestAnimationFrame(function() {
|
||||
$scope.amountHeight = amountHeight + 'px';
|
||||
$scope.contentMargin = contentMargin + 'px';
|
||||
|
|
@ -373,6 +374,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
|
|||
});
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
sendFlowService.clear();
|
||||
|
||||
configService.whenAvailable(function (config) {
|
||||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
|
||||
|
|
@ -469,4 +471,36 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun
|
|||
function rgbToHex(r, g, b) {
|
||||
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
||||
}
|
||||
|
||||
$scope.goToSend = function() {
|
||||
sendFlowService.startSend({
|
||||
fromWalletId: $scope.wallet.id
|
||||
});
|
||||
|
||||
// Go home first so that the Home tab works properly
|
||||
$state.go('tabs.home').then(function () {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.send');
|
||||
});
|
||||
|
||||
};
|
||||
$scope.goToReceive = function() {
|
||||
$state.go('tabs.home', {
|
||||
walletId: $scope.wallet.id
|
||||
}).then(function () {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.receive', {
|
||||
walletId: $scope.wallet.id
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.goToBuy = function() {
|
||||
$state.go('tabs.home', {
|
||||
walletId: $scope.wallet.id
|
||||
}).then(function () {
|
||||
$ionicHistory.clearHistory();
|
||||
$state.go('tabs.buyandsell');
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
|
|||
210
src/js/controllers/walletSelectorController.js
Normal file
210
src/js/controllers/walletSelectorController.js
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('walletSelectorController', function($scope, $rootScope, $state, $log, $ionicHistory, sendFlowService, configService, gettextCatalog, profileService, txFormatService) {
|
||||
|
||||
var fromWalletId = '';
|
||||
var priceDisplayAsFiat = false;
|
||||
var unitDecimals = 0;
|
||||
var unitsFromSatoshis = 0;
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
|
||||
$scope.$on("$ionicView.enter", onEnter);
|
||||
|
||||
function onBeforeEnter(event, data) {
|
||||
console.log('walletSelector onBeforeEnter sendflow', sendFlowService.getState());
|
||||
|
||||
if (data.direction == "back") {
|
||||
sendFlowService.popState();
|
||||
}
|
||||
|
||||
var stateParams = sendFlowService.getState();
|
||||
|
||||
var config = configService.getSync().wallet.settings;
|
||||
priceDisplayAsFiat = config.priceDisplay === 'fiat';
|
||||
unitDecimals = config.unitDecimals;
|
||||
unitsFromSatoshis = 1 / config.unitToSatoshi;
|
||||
|
||||
switch($state.current.name) {
|
||||
case 'tabs.send.wallet-to-wallet':
|
||||
$scope.sendFlowTitle = gettextCatalog.getString('Wallet to Wallet Transfer');
|
||||
break;
|
||||
case 'tabs.send.destination':
|
||||
if (stateParams.fromWalletId) {
|
||||
$scope.sendFlowTitle = gettextCatalog.getString('Wallet to Wallet Transfer');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// nop
|
||||
}
|
||||
|
||||
$scope.params = sendFlowService;
|
||||
$scope.coin = false; // Wallets to show (for destination screen or contacts)
|
||||
$scope.type = $scope.params['fromWalletId'] ? 'destination' : 'origin'; // origin || destination
|
||||
fromWalletId = $scope.params['fromWalletId'];
|
||||
|
||||
if ($scope.type === 'destination' && $scope.params.toAddress) {
|
||||
$state.transitionTo(getNextStep());
|
||||
}
|
||||
|
||||
if ($scope.params.coin) {
|
||||
$scope.coin = $scope.params.coin; // Contacts have a coin embedded
|
||||
}
|
||||
|
||||
if ($scope.params.amount) { // There is an amount, so presume that it is a payment request
|
||||
$scope.sendFlowTitle = gettextCatalog.getString('Payment Request');
|
||||
$scope.specificAmount = $scope.specificAlternativeAmount = '';
|
||||
$scope.isPaymentRequest = true;
|
||||
}
|
||||
if ($scope.params.thirdParty) {
|
||||
$scope.thirdParty = $scope.params.thirdParty;
|
||||
}
|
||||
};
|
||||
|
||||
function onEnter (event, data) {
|
||||
configService.whenAvailable(function(config) {
|
||||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
|
||||
});
|
||||
|
||||
if ($scope.thirdParty) {
|
||||
// Third party services specific logic
|
||||
handleThirdPartyIfShapeshift();
|
||||
}
|
||||
|
||||
prepareWalletLists();
|
||||
formatRequestedAmount();
|
||||
};
|
||||
|
||||
function formatRequestedAmount() {
|
||||
if ($scope.params.amount) {
|
||||
var cryptoAmount = (unitsFromSatoshis * $scope.params.amount).toFixed(unitDecimals);
|
||||
var cryptoCoin = $scope.coin.toUpperCase();
|
||||
|
||||
txFormatService.formatAlternativeStr($scope.coin, $scope.params.amount, function onFormatAlternativeStr(formatted){
|
||||
if (formatted) {
|
||||
var fiatParts = formatted.split(' ');
|
||||
var fiatAmount = fiatParts[0];
|
||||
var fiatCurrrency = fiatParts.length > 1 ? fiatParts[1] : '';
|
||||
|
||||
if (priceDisplayAsFiat) {
|
||||
$scope.requestAmount = fiatAmount;
|
||||
$scope.requestCurrency = fiatCurrrency;
|
||||
|
||||
$scope.requestAmountSecondary = cryptoAmount;
|
||||
$scope.requestCurrencySecondary = cryptoCoin;
|
||||
} else {
|
||||
$scope.requestAmount = cryptoAmount;
|
||||
$scope.requestCurrency = cryptoCoin;
|
||||
|
||||
$scope.requestAmountSecondary = fiatAmount;
|
||||
$scope.requestCurrencySecondary = fiatCurrrency;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getNextStep() {
|
||||
if ($scope.thirdParty) {
|
||||
$scope.params.thirdParty = $scope.thirdParty
|
||||
}
|
||||
if (!$scope.params.toWalletId && !$scope.params.toAddress) { // If we have no toAddress or fromWallet
|
||||
return 'tabs.send.destination';
|
||||
} else if (!$scope.params.amount) { // If we have no amount
|
||||
return 'tabs.send.amount';
|
||||
} else { // If we do have them
|
||||
return 'tabs.send.review';
|
||||
}
|
||||
}
|
||||
|
||||
function handleThirdPartyIfShapeshift() {
|
||||
console.log($scope.thirdParty, $scope.coin);
|
||||
if ($scope.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the
|
||||
$scope.coin = profileService.getWallet(fromWalletId).coin;
|
||||
if ($scope.coin === 'bch') {
|
||||
$scope.coin = 'btc';
|
||||
} else {
|
||||
$scope.coin = 'bch';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prepareWalletLists() {
|
||||
var walletsAll = [];
|
||||
var walletsSufficientFunds = [];
|
||||
$scope.walletsInsufficientFunds = []; // For origin screen
|
||||
|
||||
if ($scope.type === 'origin') {
|
||||
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send from');
|
||||
|
||||
if ($scope.params.amount) {
|
||||
|
||||
walletsAll = profileService.getWallets({coin: $scope.coin});
|
||||
|
||||
walletsAll.forEach(function forWallet(wallet){
|
||||
if (wallet.status.availableBalanceSat > $scope.params.amount) {
|
||||
walletsSufficientFunds.push(wallet);
|
||||
} else {
|
||||
$scope.walletsInsufficientFunds.push(wallet);
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.coin === 'btc') {
|
||||
$scope.walletsBtc = walletsSufficientFunds;
|
||||
} else {
|
||||
$scope.walletsBch = walletsSufficientFunds;
|
||||
}
|
||||
|
||||
} else if ($scope.coin) {
|
||||
walletsAll = profileService.getWallets({coin: $scope.coin});
|
||||
walletsAll.forEach(function forWallet(wallet){
|
||||
if (wallet.status.availableBalanceSat > 0) {
|
||||
walletsSufficientFunds.push(wallet);
|
||||
} else {
|
||||
$scope.walletsInsufficientFunds.push(wallet);
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.coin === 'btc') {
|
||||
$scope.walletsBtc = walletsSufficientFunds;
|
||||
} else {
|
||||
$scope.walletsBch = walletsSufficientFunds;
|
||||
}
|
||||
} else {
|
||||
$scope.walletsBch = profileService.getWallets({coin: 'bch', hasFunds: true});
|
||||
$scope.walletsBtc = profileService.getWallets({coin: 'btc', hasFunds: true});
|
||||
$scope.walletsInsufficientFunds = profileService.getWallets({coin: $scope.coin, hasNoFunds: true});
|
||||
}
|
||||
|
||||
} else if ($scope.type === 'destination') {
|
||||
if (!$scope.coin) { // Allow for the coin to be set by a third party
|
||||
$scope.fromWallet = profileService.getWallet(fromWalletId);
|
||||
$scope.coin = $scope.fromWallet.coin; // Only show wallets with the select origin wallet coin
|
||||
}
|
||||
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send to');
|
||||
|
||||
if ($scope.coin === 'btc') { // if no specific coin is set or coin is set btc
|
||||
$scope.walletsBtc = profileService.getWallets({coin: $scope.coin});
|
||||
} else {
|
||||
$scope.walletsBch = profileService.getWallets({coin: $scope.coin});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
$scope.useWallet = function(wallet) {
|
||||
var params = sendFlowService.getState();
|
||||
if ($scope.type === 'origin') { // we're on the origin screen, set wallet to send from
|
||||
params.fromWalletId = wallet.id;
|
||||
} else { // we're on the destination screen, set wallet to send to
|
||||
params.toWalletId = wallet.id;
|
||||
}
|
||||
sendFlowService.pushState(params);
|
||||
$state.transitionTo(getNextStep(), $scope.params);
|
||||
};
|
||||
|
||||
$scope.goBack = function() {
|
||||
$ionicHistory.goBack();
|
||||
}
|
||||
|
||||
});
|
||||
177
src/js/directives/formattedAmount.js
Normal file
177
src/js/directives/formattedAmount.js
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @desc amount directive that can be used to display formatted financial values
|
||||
* size-equal attribute is optional, defaults to false.
|
||||
* @example fee = {
|
||||
* value: 12.49382901,
|
||||
* currency: 'BCH'
|
||||
* }
|
||||
* @example <formatted-amount value="fee.value" currency="fee.currency"></formatted-amount>
|
||||
* @example <formatted-amount value="fee.value" currency="fee.currency" size-equal="true"></formatted-amount>
|
||||
*/
|
||||
angular.module('bitcoincom.directives')
|
||||
.directive('formattedAmount', function(uxLanguage) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {
|
||||
value: '@',
|
||||
currency: '@',
|
||||
sizeEqual: '@'
|
||||
},
|
||||
templateUrl: 'views/includes/formatted-amount.html',
|
||||
controller: function($scope, $timeout) {
|
||||
$scope.canShow = false;
|
||||
|
||||
$scope.displaySizeEqual = !!$scope.sizeEqual;
|
||||
|
||||
$timeout(function onFormattedAmountTimeout() {
|
||||
|
||||
var decimalPlaces = {
|
||||
'0': ['BIF', 'CLP', 'DJF', 'GNF', 'ILS', 'JPY', 'KMF', 'KRW', 'MGA', 'PYG', 'RWF', 'UGX', 'VND', 'VUV', 'XAF', 'XOF', 'XPF'],
|
||||
'3': ['BHD', 'IQD', 'JOD', 'KWD', 'OMR', 'TND'],
|
||||
'8': ['BCH', 'BTC']
|
||||
};
|
||||
var localizeNumbers = function(x, minimumFractionDigits) {
|
||||
var parsed = parseFloat(x);
|
||||
var opts = {
|
||||
minimumFractionDigits: minimumFractionDigits,
|
||||
useGrouping: true
|
||||
};
|
||||
var lang = uxLanguage.getCurrentLanguage();
|
||||
var localized = parsed.toLocaleString(lang, opts);
|
||||
var corrected = ensureEnoughFractionalDigits(localized, x, minimumFractionDigits);
|
||||
return corrected;
|
||||
};
|
||||
|
||||
var buildAmount = function(start, middle, end) {
|
||||
$scope.start = start;
|
||||
$scope.middle = middle;
|
||||
$scope.end = end;
|
||||
};
|
||||
|
||||
var getDecimalPlaces = function(currency) {
|
||||
if (decimalPlaces['0'].indexOf(currency.toUpperCase()) > -1) return '0';
|
||||
if (decimalPlaces['3'].indexOf(currency.toUpperCase()) > -1) return '3';
|
||||
if (decimalPlaces['8'].indexOf(currency.toUpperCase()) > -1) return '8';
|
||||
return '2';
|
||||
};
|
||||
|
||||
var getDecimalSeparator = function() {
|
||||
var testNum = 1.5;
|
||||
var testString = testNum.toLocaleString(uxLanguage.getCurrentLanguage());
|
||||
// Some environments let you set decimal separators that are more than one character
|
||||
var separator = /^1(.+)5$/.exec(testString)[1]
|
||||
return separator;
|
||||
};
|
||||
|
||||
var formatNumbers = function() {
|
||||
// During watch, may be changed from having a separate currency value,
|
||||
// to both being in value. Don't want to use previous currency value.
|
||||
// Try to extract currency from value..
|
||||
var currencySplit = $scope.value.split(" ");
|
||||
if (currencySplit.length === 2) {
|
||||
$scope.currency = currencySplit[1];
|
||||
}
|
||||
$scope.currency = $scope.currency || '';
|
||||
|
||||
|
||||
var parsed = parseFloat($scope.value);
|
||||
var valueFormatted = '';
|
||||
var valueProcessing = '';
|
||||
switch (getDecimalPlaces($scope.currency)) {
|
||||
case '0':
|
||||
if (isNaN(parsed)) {
|
||||
buildAmount('-', '', '');
|
||||
} else {
|
||||
valueFormatted = localizeNumbers(Math.round(parsed), 0);
|
||||
buildAmount(valueFormatted, '', '');
|
||||
}
|
||||
break;
|
||||
|
||||
case '3':
|
||||
if (isNaN(parsed)) {
|
||||
buildAmount('-' + getDecimalSeparator() + '---', '', '');
|
||||
} else {
|
||||
valueProcessing = parsed.toFixed(3);
|
||||
valueFormatted = localizeNumbers(valueProcessing, 3);
|
||||
buildAmount(valueFormatted, '', '');
|
||||
}
|
||||
break;
|
||||
|
||||
case '8':
|
||||
if (isNaN(parsed)) {
|
||||
buildAmount('-' + getDecimalSeparator() + '---', '', '');
|
||||
} else if (parsed === 0) {
|
||||
buildAmount('0', '', '');
|
||||
} else {
|
||||
valueFormatted = parsed.toFixed(8);
|
||||
valueFormatted = localizeNumbers(valueFormatted, 8);
|
||||
var start = valueFormatted.slice(0, -5);
|
||||
var middle = valueFormatted.slice(-5, -2);
|
||||
var end = valueFormatted.substr(valueFormatted.length - 2);
|
||||
buildAmount(start, middle, end);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default: // 2
|
||||
if (isNaN(parsed)) {
|
||||
buildAmount('-' + getDecimalSeparator() + '--', '', '');
|
||||
} else {
|
||||
valueProcessing = parseFloat(parsed.toFixed(2));
|
||||
valueFormatted = localizeNumbers(valueProcessing, 2);
|
||||
buildAmount(valueFormatted, '', '');
|
||||
}
|
||||
break;
|
||||
}
|
||||
$scope.canShow = true;
|
||||
};
|
||||
|
||||
formatNumbers();
|
||||
$scope.$watchGroup(['currency', 'value'], function onFormattedAmountWatch() {
|
||||
formatNumbers();
|
||||
});
|
||||
|
||||
/**
|
||||
* On Android 4.4, toLocaleString() only returns 3 fractional digits when 8 is specified.
|
||||
*/
|
||||
function ensureEnoughFractionalDigits(localizedString, number, desiredFractionDigits) {
|
||||
if (desiredFractionDigits === 0) {
|
||||
// Assume it is OK
|
||||
return localizedString;
|
||||
}
|
||||
var fractionalRe = /^(\d*\D)(\d+)$/;
|
||||
var match = fractionalRe.exec(localizedString);
|
||||
if (match.length !== 3) {
|
||||
// Don't know what's happening, just return what we have
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
var decimals = match[2];
|
||||
var decimalCount = decimals.length;
|
||||
if (decimalCount >= desiredFractionDigits) {
|
||||
// Everything is OK.
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
if (typeof number !== 'number') {
|
||||
number = parseFloat(number);
|
||||
}
|
||||
|
||||
var fixed = number.toFixed(desiredFractionDigits);
|
||||
var fixedMatch = fractionalRe.exec(fixed);
|
||||
if (fixedMatch.length !== 3) {
|
||||
// Don't know what's happening, just return what we have
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
// Keeps locale decimal separator.
|
||||
var enough = match[1] + fixedMatch[2];
|
||||
return enough;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
);
|
||||
|
|
@ -111,7 +111,7 @@ angular.module('copayApp.directives').directive('shapeshiftCoinTrader', function
|
|||
orderId: $scope.depositInfo.orderId
|
||||
};
|
||||
|
||||
if (incomingData.redir(sendAddress, shapeshiftData)) {
|
||||
if (incomingData.redir(sendAddress, 'shapeshift', shapeshiftData)) {
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ angular.module('copayApp.directives')
|
|||
scope: {
|
||||
isShown: '=slideSuccessShow',
|
||||
onConfirm: '&slideSuccessOnConfirm',
|
||||
hideOnConfirm: '=slideSuccessHideOnConfirm'
|
||||
hideOnConfirm: '=slideSuccessHideOnConfirm',
|
||||
onShare: '=slideSuccessOnShare',
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
scope.isWindowsPhoneApp = platformInfo.isCordova && platformInfo.isWP;
|
||||
|
||||
scope.isCordova = platformInfo.isCordova;
|
||||
scope.hasShareFunction = typeof scope.onShare === 'function';
|
||||
var elm = element[0];
|
||||
elm.style.display = 'none';
|
||||
scope.$watch('isShown', function() {
|
||||
|
|
@ -32,6 +32,9 @@ angular.module('copayApp.directives')
|
|||
elm.style.display = 'none';
|
||||
}
|
||||
};
|
||||
scope.onShareButtonClick = function() {
|
||||
scope.onShare();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
})
|
||||
.state('tabs.receive', {
|
||||
url: '/receive',
|
||||
url: '/receive/:walletId',
|
||||
views: {
|
||||
'tab-receive': {
|
||||
controller: 'tabReceiveController',
|
||||
|
|
@ -287,16 +287,44 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
*/
|
||||
|
||||
.state('tabs.send.amount', {
|
||||
url: '/amount/:recipientType/:toAddress/:toName/:toEmail/:toColor/:coin/:fixedUnit/:fromWalletId/:minShapeshiftAmount/:maxShapeshiftAmount/:shapeshiftOrderId/:displayAddress/:noPrefix',
|
||||
url: '/amount',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send.wallet-to-wallet', {
|
||||
url: '/wallet-to-wallet',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'walletSelectorController',
|
||||
templateUrl: 'views/walletSelector.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send.origin', {
|
||||
url: '/origin',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'walletSelectorController',
|
||||
templateUrl: 'views/walletSelector.html',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send.destination', {
|
||||
url: '/destination',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'walletSelectorController',
|
||||
templateUrl: 'views/walletSelector.html',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send.confirm', {
|
||||
url: '/confirm/:recipientType/:toAddress/:toName/:toAmount/:toEmail/:toColor/:description/:coin/:useSendMax/:fromWalletId/:displayAddress/:requiredFeeRate',
|
||||
url: '/confirm',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'confirmController',
|
||||
|
|
@ -316,6 +344,19 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send.review', {
|
||||
url: '/review/:thirdParty/:amount/:fromWalletId/:sendMax/:toAddress/:toWalletId',
|
||||
views: {
|
||||
'tab-send@tabs': {
|
||||
controller: 'reviewController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/review.html'
|
||||
}
|
||||
},
|
||||
params: {
|
||||
paypro: null
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
@ -695,16 +736,17 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
})
|
||||
|
||||
.state('tabs.paymentRequest.amount', {
|
||||
url: '/amount/:coin',
|
||||
url: '/amount/:toWalletId',
|
||||
views: {
|
||||
'tab-receive@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.paymentRequest.confirm', {
|
||||
url: '/confirm/:amount/:currency/:coin',
|
||||
url: '/confirm/:amount/:toWalletId',
|
||||
views: {
|
||||
'tab-receive@tabs': {
|
||||
controller: 'customAmountController',
|
||||
|
|
@ -845,6 +887,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
|
|
@ -910,6 +953,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
|
|
@ -968,7 +1012,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
|
||||
/* Shapeshift */
|
||||
.state('tabs.shapeshift', {
|
||||
url: '/shapeshift',
|
||||
url: '/shapeshift/:fromWalletId/:toWalletId',
|
||||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'shapeshiftController',
|
||||
|
|
@ -1029,6 +1073,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
},
|
||||
|
|
@ -1081,6 +1126,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
},
|
||||
|
|
@ -1137,6 +1183,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
views: {
|
||||
'tab-home@tabs': {
|
||||
controller: 'amountController',
|
||||
controllerAs: 'vm',
|
||||
templateUrl: 'views/amount.html'
|
||||
}
|
||||
}
|
||||
|
|
@ -1160,7 +1207,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
});
|
||||
})
|
||||
.run(function($rootScope, $state, $location, $log, $timeout, startupService, ionicToast, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ buydotbitcoindotcomService, glideraService, amazonService, bitpayCardService, applicationService, mercadoLibreService, rateService) {
|
||||
.run(function($rootScope, $state, $location, $log, $timeout, startupService, ionicToast, fingerprintService, $ionicHistory, $ionicPlatform, $window, appConfigService, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService, configService, emailService, /* plugins START HERE => */ buydotbitcoindotcomService, pushNotificationsService, glideraService, amazonService, bitpayCardService, applicationService, mercadoLibreService, rateService) {
|
||||
|
||||
$ionicPlatform.ready(function() {
|
||||
|
||||
|
|
@ -1184,9 +1231,14 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
});
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
configService.whenAvailable(function(config) {
|
||||
pushNotificationsService.init();
|
||||
});
|
||||
//firebaseEventsService.init();
|
||||
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
|
||||
// Send a log to test
|
||||
|
|
@ -1264,7 +1316,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
if (screen.width < 768 && platformInfo.isCordova)
|
||||
screen.lockOrientation('portrait');
|
||||
|
||||
if (ionic.Platform.isAndroid() && StatusBar) {
|
||||
if (ionic.Platform.isAndroid() && platformInfo.isCordova && StatusBar) {
|
||||
StatusBar.backgroundColorByHexString('#000000');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services').factory('bitcoincomService', function(platformInfo, nextStepsService) {
|
||||
angular.module('copayApp.services').factory('bitcoincomService', function(gettextCatalog, nextStepsService, platformInfo) {
|
||||
var root = {};
|
||||
var credentials = {};
|
||||
|
||||
|
|
@ -19,42 +19,42 @@ angular.module('copayApp.services').factory('bitcoincomService', function(platfo
|
|||
|
||||
var cashGamesItem = {
|
||||
name: 'games',
|
||||
title: 'Bitcoin Cash Games',
|
||||
title: gettextCatalog.getString('Bitcoin Cash Games'),
|
||||
icon: 'icon-games',
|
||||
href: 'https://cashgames.bitcoin.com'
|
||||
};
|
||||
|
||||
var newsItem = {
|
||||
name: 'news',
|
||||
title: 'News',
|
||||
title: gettextCatalog.getString('News'),
|
||||
icon: 'icon-news',
|
||||
href: 'https://news.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=News'
|
||||
};
|
||||
|
||||
var poolItem = {
|
||||
name: 'pool',
|
||||
title: 'Mining Pool',
|
||||
title: gettextCatalog.getString('Mining Pool'),
|
||||
icon: 'icon-mining',
|
||||
href: 'https://pool.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Pool'
|
||||
};
|
||||
|
||||
var toolsItem = {
|
||||
name: 'tools',
|
||||
title: 'Tools',
|
||||
title: gettextCatalog.getString('Tools'),
|
||||
icon: 'icon-tools',
|
||||
href: 'https://tools.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Tools'
|
||||
};
|
||||
|
||||
var priceChartItem = {
|
||||
name: 'pricechart',
|
||||
title: 'Bitcoin Price Charts',
|
||||
title: gettextCatalog.getString('Bitcoin Price Charts'),
|
||||
icon: 'icon-chart',
|
||||
sref: 'tabs.pricechart',
|
||||
};
|
||||
|
||||
var faucetItem = {
|
||||
name: 'faucet',
|
||||
title: 'Free Bitcoin Cash',
|
||||
title: gettextCatalog.getString('Free Bitcoin Cash'),
|
||||
icon: 'icon-faucet',
|
||||
href: 'https://free.bitcoin.com/?utm_source=WalletApp&utm_medium=' + os + '&utm_campaign=Faucet'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('buyAndSellService', function($log, servicesService, lodash, $ionicScrollDelegate, $timeout) {
|
||||
angular.module('copayApp.services').factory('buyAndSellService', function(gettextCatalog, $log, servicesService, lodash, $ionicScrollDelegate, $timeout) {
|
||||
var root = {};
|
||||
var services = [];
|
||||
var linkedServices = [];
|
||||
|
|
@ -23,7 +23,7 @@ angular.module('copayApp.services').factory('buyAndSellService', function($log,
|
|||
|
||||
if (linkedServices.length == 0) {
|
||||
servicesService.register({
|
||||
title: 'Buy Bitcoin',
|
||||
title: gettextCatalog.getString('Buy Bitcoin'),
|
||||
name: 'buyandsell',
|
||||
icon: 'icon-buy-bitcoin2',
|
||||
sref: 'tabs.buyandsell',
|
||||
|
|
|
|||
|
|
@ -11,10 +11,15 @@ angular.module('copayApp.services').factory('clipboardService', function ($http,
|
|||
cordova.plugins.clipboard.copy(data);
|
||||
} else if (platformInfo.isNW) {
|
||||
nodeWebkitService.writeToClipboard(data);
|
||||
} else if (navigator && navigator.clipboard) {
|
||||
$log.debug("Use navigator clipboard.")
|
||||
navigator.clipboard.writeText(data).catch(function onClipboardError(err) {
|
||||
$log.debug("Clipboard writing is not supported in your browser..");
|
||||
});
|
||||
} else if (clipboard.supported) {
|
||||
clipboard.copyText(data);
|
||||
} else {
|
||||
// No supported
|
||||
// Not supported
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
'use strict'
|
||||
angular.module('copayApp.services').factory('communityService', function(configService, $log, lodash) {
|
||||
angular.module('copayApp.services').factory('communityService', function(configService, gettextCatalog, $log, lodash) {
|
||||
var root = {};
|
||||
var services = [];
|
||||
|
||||
|
|
@ -37,14 +37,14 @@ angular.module('copayApp.services').factory('communityService', function(configS
|
|||
|
||||
var bchRedditItem = {
|
||||
name: 'bchreddit',
|
||||
title: 'Bitcoin Cash Reddit',
|
||||
title: gettextCatalog.getString('Bitcoin Cash Reddit'),
|
||||
icon: 'icon-reddit-white',
|
||||
href: 'http://reddit.com/r/btc'
|
||||
};
|
||||
|
||||
var bitcoincomTwitterItem = {
|
||||
name: 'bitcoincomTwitter',
|
||||
title: 'Bitcoin.com Twitter',
|
||||
title: gettextCatalog.getString('Bitcoin.com Twitter'),
|
||||
icon: 'icon-twitter-white',
|
||||
href: 'https://twitter.com/BTCTN'
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services').factory('firebaseEventsService', function firebaseEventsService($log, $state, $ionicHistory, sjcl, platformInfo, lodash, appConfigService, profileService, configService) {
|
||||
angular.module('copayApp.services').factory('firebaseEventsService', function firebaseEventsService($log, platformInfo) {
|
||||
var root = {};
|
||||
var useEvents = platformInfo.isCordova && !platformInfo.isWP;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) {
|
||||
angular.module('copayApp.services').factory('incomingData', function($log, $state, $timeout, $ionicHistory, bitcore, bitcoreCash, $rootScope, payproService, scannerService, sendFlowService, appConfigService, popupService, gettextCatalog, bitcoinCashJsService) {
|
||||
|
||||
var root = {};
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
$rootScope.$broadcast('incomingDataMenu.showMenu', data);
|
||||
};
|
||||
|
||||
root.redir = function(data, shapeshiftData) {
|
||||
root.redir = function(data, serviceId, serviceData) {
|
||||
var originalAddress = null;
|
||||
var noPrefixInAddress = 0;
|
||||
|
||||
|
|
@ -75,35 +75,41 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
return true;
|
||||
}
|
||||
|
||||
function goSend(addr, amount, message, coin, shapeshiftData) {
|
||||
function goSend(addr, amount, message, coin, serviceId, serviceData) {
|
||||
$state.go('tabs.send', {}, {
|
||||
'reload': true,
|
||||
'notify': $state.current.name == 'tabs.send' ? false : true
|
||||
});
|
||||
// Timeout is required to enable the "Back" button
|
||||
$timeout(function() {
|
||||
var params = sendFlowService.getState();
|
||||
|
||||
if (amount) {
|
||||
$state.transitionTo('tabs.send.confirm', {
|
||||
toAmount: amount,
|
||||
toAddress: addr,
|
||||
displayAddress: originalAddress ? originalAddress : addr,
|
||||
description: message,
|
||||
coin: coin
|
||||
});
|
||||
params.amount = amount;
|
||||
}
|
||||
|
||||
if (addr) {
|
||||
params.toAddress = addr;
|
||||
params.displayAddress = originalAddress ? originalAddress : addr;
|
||||
}
|
||||
|
||||
if (coin) {
|
||||
params.coin = coin;
|
||||
}
|
||||
|
||||
if (noPrefixInAddress) {
|
||||
params.noPrefixInAddress = noPrefixInAddress;
|
||||
}
|
||||
|
||||
if (serviceId) {
|
||||
params.thirdParty = [];
|
||||
params.thirdParty.id = serviceId;
|
||||
params.thirdParty.data = serviceData;
|
||||
sendFlowService.pushState(params);
|
||||
$state.transitionTo('tabs.send.amount');
|
||||
} else {
|
||||
var params = {
|
||||
toAddress: addr,
|
||||
coin: coin,
|
||||
displayAddress: originalAddress ? originalAddress : addr,
|
||||
noPrefix: noPrefixInAddress
|
||||
};
|
||||
if (shapeshiftData) {
|
||||
params['fromWalletId'] = shapeshiftData.fromWalletId;
|
||||
params['minShapeshiftAmount'] = shapeshiftData.minAmount;
|
||||
params['maxShapeshiftAmount'] = shapeshiftData.maxAmount;
|
||||
params['shapeshiftOrderId'] = shapeshiftData.orderId;
|
||||
}
|
||||
$state.transitionTo('tabs.send.amount', params);
|
||||
sendFlowService.pushState(params);
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
|
@ -112,15 +118,20 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
var coin = data.indexOf('bitcoincash') >= 0 ? 'bch' : 'btc';
|
||||
data = decodeURIComponent(data.replace(/bitcoin(cash)?:\?r=/, ''));
|
||||
if (coin == 'bch') {
|
||||
payproService.getPayProDetailsViaHttp(data, function(err, details) {
|
||||
payproService.getPayProDetailsViaHttp(data, function onGetPayProDetailsViaHttp(err, details) {
|
||||
if (err) {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err)
|
||||
var message = err.toString();
|
||||
if (typeof err.data === 'string') {
|
||||
// i.e. 'This invoice is no longer accepting payments'
|
||||
message = gettextCatalog.getString(err.data);
|
||||
}
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), message)
|
||||
} else {
|
||||
handlePayPro(createBchPayProObject(details), coin);
|
||||
handlePayPro(details, coin);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
payproService.getPayProDetails(data, coin, function(err, details) {
|
||||
payproService.getPayProDetails(data, coin, function onGetPayProDetails(err, details) {
|
||||
if (err) {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
} else {
|
||||
|
|
@ -146,12 +157,12 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
if (parsed.r) {
|
||||
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
|
||||
if (err) {
|
||||
if (addr && amount) goSend(addr, amount, message, coin, shapeshiftData);
|
||||
if (addr && amount) goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
else popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
} else handlePayPro(details, coin);
|
||||
});
|
||||
} else {
|
||||
goSend(addr, amount, message, coin, shapeshiftData);
|
||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
}
|
||||
return true;
|
||||
// Cash URI
|
||||
|
|
@ -169,14 +180,14 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
|
||||
if (err) {
|
||||
if (addr && amount)
|
||||
goSend(addr, amount, message, coin, shapeshiftData);
|
||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
else
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
}
|
||||
handlePayPro(details, coin);
|
||||
});
|
||||
} else {
|
||||
goSend(addr, amount, message, coin, shapeshiftData);
|
||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
}
|
||||
return true;
|
||||
|
||||
|
|
@ -212,14 +223,14 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
payproService.getPayProDetails(parsed.r, coin, function(err, details) {
|
||||
if (err) {
|
||||
if (addr && amount)
|
||||
goSend(addr, amount, message, coin, shapeshiftData);
|
||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
else
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||
}
|
||||
handlePayPro(details, coin);
|
||||
});
|
||||
} else {
|
||||
goSend(addr, amount, message, coin, shapeshiftData);
|
||||
goSend(addr, amount, message, coin, serviceId, serviceData);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
@ -377,46 +388,71 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
'notify': $state.current.name == 'tabs.send' ? false : true
|
||||
});
|
||||
$timeout(function() {
|
||||
$state.transitionTo('tabs.send.amount', {
|
||||
var stateParams = {
|
||||
toAddress: toAddress,
|
||||
coin: coin,
|
||||
noPrefix: 1
|
||||
});
|
||||
};
|
||||
sendFlowService.pushState(stateParams);
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function createBchPayProObject(payProData) {
|
||||
var displayAddr = payProData.outputs[0].address;
|
||||
var toAddr = bitcoinCashJsService.readAddress('bitcoincash:' + displayAddr).legacy;
|
||||
return {
|
||||
amount: payProData.outputs[0].amount,
|
||||
function handlePayPro(payProData, coin) {
|
||||
|
||||
console.log(payProData);
|
||||
|
||||
var toAddr = payProData.toAddress;
|
||||
var amount = payProData.amount;
|
||||
var paymentUrl = payProData.url;
|
||||
var expires = payProData.expires;
|
||||
var time = payProData.time;
|
||||
|
||||
if (coin === 'bch') {
|
||||
var displayAddr = payProData.outputs[0].address;
|
||||
toAddr = bitcoinCashJsService.readAddress('bitcoincash:' + displayAddr).legacy;
|
||||
amount = payProData.outputs[0].amount;
|
||||
paymentUrl = payProData.paymentUrl;
|
||||
expires = Math.floor(new Date(expires).getTime() / 1000)
|
||||
time = Math.ceil(new Date(time).getTime() / 1000)
|
||||
}
|
||||
|
||||
var name = payProData.domain;
|
||||
|
||||
if (payProData.memo.indexOf('eGifter') > -1) {
|
||||
name = 'eGifter'
|
||||
} else if (paymentUrl.indexOf('https://bitpay.com') > -1) {
|
||||
name = 'BitPay';
|
||||
}
|
||||
|
||||
var thirdPartyData = {
|
||||
id: 'bip70',
|
||||
amount: amount,
|
||||
caTrusted: true,
|
||||
domain: 'bitpay.com',
|
||||
expires: Math.floor(new Date(payProData.expires).getTime() / 1000),
|
||||
name: name,
|
||||
domain: payProData.domain,
|
||||
expires: expires,
|
||||
memo: payProData.memo,
|
||||
network: 'livenet',
|
||||
requiredFeeRate: payProData.requiredFeeRate,
|
||||
selfSigned: 0,
|
||||
time: Math.ceil(new Date(payProData.time).getTime() / 1000),
|
||||
time: time,
|
||||
displayAddress: displayAddr,
|
||||
toAddress: toAddr,
|
||||
url: payProData.paymentUrl,
|
||||
url: paymentUrl,
|
||||
verified: true
|
||||
};
|
||||
}
|
||||
|
||||
function handlePayPro(payProDetails, coin) {
|
||||
var stateParams = {
|
||||
toAmount: payProDetails.amount,
|
||||
toAddress: payProDetails.toAddress,
|
||||
description: payProDetails.memo,
|
||||
paypro: payProDetails,
|
||||
amount: thirdPartyData.amount,
|
||||
toAddress: thirdPartyData.toAddress,
|
||||
coin: coin,
|
||||
thirdParty: JSON.stringify(thirdPartyData)
|
||||
};
|
||||
|
||||
// fee
|
||||
if (payProDetails.requiredFeeRate) {
|
||||
stateParams.requiredFeeRate = payProDetails.requiredFeeRate * 1024;
|
||||
if (thirdPartyData.requiredFeeRate) {
|
||||
stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024;
|
||||
}
|
||||
|
||||
scannerService.pausePreview();
|
||||
|
|
@ -425,7 +461,8 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat
|
|||
'notify': $state.current.name == 'tabs.send' ? false : true
|
||||
}).then(function() {
|
||||
$timeout(function() {
|
||||
$state.transitionTo('tabs.send.confirm', stateParams);
|
||||
sendFlowService.pushState(stateParams); // Need to do more here
|
||||
$state.transitionTo('tabs.send.origin');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
78
src/js/services/paypro.service.js
Normal file
78
src/js/services/paypro.service.js
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
'use strict';
|
||||
|
||||
// For BIP70 Payment Protocol
|
||||
|
||||
angular
|
||||
.module('copayApp.services')
|
||||
.factory('payproService', payproService);
|
||||
|
||||
function payproService(gettextCatalog, $http, $log, ongoingProcess, platformInfo, profileService) {
|
||||
|
||||
var service = {
|
||||
getPayProDetails: getPayProDetails,
|
||||
getPayProDetailsViaHttp: getPayProDetailsViaHttp,
|
||||
broadcastBchTx: broadcastBchTx
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
function getPayProDetails(uri, coin, cb, disableLoader) {
|
||||
if (!cb) cb = function() {};
|
||||
|
||||
var wallet = profileService.getWallets({
|
||||
onlyComplete: true,
|
||||
coin: coin
|
||||
})[0];
|
||||
|
||||
if (!wallet) return cb();
|
||||
|
||||
if (platformInfo.isChromeApp) {
|
||||
return cb(gettextCatalog.getString('Payment Protocol not supported on Chrome App'));
|
||||
}
|
||||
|
||||
$log.debug('Fetch PayPro Request...', uri);
|
||||
|
||||
if (!disableLoader) ongoingProcess.set('fetchingPayPro', true);
|
||||
|
||||
wallet.fetchPayPro({
|
||||
payProUrl: uri,
|
||||
}, function(err, paypro) {
|
||||
if (!disableLoader) ongoingProcess.set('fetchingPayPro', false);
|
||||
if (err) return cb(gettextCatalog.getString('Could Not Fetch Payment: Check if it is still valid'));
|
||||
else if (!paypro.verified) {
|
||||
$log.warn('Failed to verify payment protocol signatures');
|
||||
return cb(gettextCatalog.getString('Payment Protocol Invalid'));
|
||||
}
|
||||
return cb(null, paypro);
|
||||
});
|
||||
}
|
||||
|
||||
function getPayProDetailsViaHttp(uri, cb) {
|
||||
var config = {
|
||||
headers: {'Accept': 'application/payment-request'}
|
||||
};
|
||||
$http.get(uri, config).then(function onGetPayProDetailsSuccess(response) {
|
||||
return cb(null, response.data);
|
||||
}, function onGetPayProDetailsError(error) {
|
||||
return cb(error, null);
|
||||
});
|
||||
}
|
||||
|
||||
function broadcastBchTx(signedTxp, cb) {
|
||||
var config = {
|
||||
headers: {'Content-Type': 'application/payment'}
|
||||
};
|
||||
|
||||
var data = {
|
||||
currency: 'BCH',
|
||||
transactions: [signedTxp.raw]
|
||||
};
|
||||
|
||||
$http.post(signedTxp.payProUrl, data, config).then(function(response) {
|
||||
signedTxp.response = response.data;
|
||||
return cb(null, signedTxp);
|
||||
}, function(error) {
|
||||
return cb(error.data, null);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('payproService',
|
||||
function(profileService, platformInfo, gettextCatalog, ongoingProcess, $log, $http) {
|
||||
|
||||
var ret = {};
|
||||
|
||||
ret.getPayProDetails = function(uri, coin, cb, disableLoader) {
|
||||
if (!cb) cb = function() {};
|
||||
|
||||
var wallet = profileService.getWallets({
|
||||
onlyComplete: true,
|
||||
coin: coin
|
||||
})[0];
|
||||
|
||||
if (!wallet) return cb();
|
||||
|
||||
if (platformInfo.isChromeApp) {
|
||||
return cb(gettextCatalog.getString('Payment Protocol not supported on Chrome App'));
|
||||
}
|
||||
|
||||
$log.debug('Fetch PayPro Request...', uri);
|
||||
|
||||
if (!disableLoader) ongoingProcess.set('fetchingPayPro', true);
|
||||
|
||||
wallet.fetchPayPro({
|
||||
payProUrl: uri,
|
||||
}, function(err, paypro) {
|
||||
if (!disableLoader) ongoingProcess.set('fetchingPayPro', false);
|
||||
if (err) return cb(gettextCatalog.getString('Could Not Fetch Payment: Check if it is still valid'));
|
||||
else if (!paypro.verified) {
|
||||
$log.warn('Failed to verify payment protocol signatures');
|
||||
return cb(gettextCatalog.getString('Payment Protocol Invalid'));
|
||||
}
|
||||
return cb(null, paypro);
|
||||
});
|
||||
};
|
||||
|
||||
ret.getPayProDetailsViaHttp = function(uri, cb) {
|
||||
var config = {
|
||||
headers: {'Accept': 'application/payment-request'}
|
||||
};
|
||||
$http.get(uri, config).then(function(response) {
|
||||
return cb(null, response.data);
|
||||
}, function(error) {
|
||||
return cb(error, null);
|
||||
});
|
||||
}
|
||||
|
||||
ret.broadcastBchTx = function(signedTxp, cb) {
|
||||
var config = {
|
||||
headers: {'Content-Type': 'application/payment'}
|
||||
};
|
||||
|
||||
var data = {
|
||||
currency: 'BCH',
|
||||
transactions: [signedTxp.raw]
|
||||
};
|
||||
|
||||
$http.post(signedTxp.payProUrl, data, config).then(function(response) {
|
||||
signedTxp.response = response.data;
|
||||
return cb(null, signedTxp);
|
||||
}, function(error) {
|
||||
return cb(error.data, null);
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
});
|
||||
|
|
@ -427,9 +427,9 @@ angular.module('copayApp.services')
|
|||
}, function(err, secret) {
|
||||
if (err) return bwcError.cb(err, gettextCatalog.getString('Error creating wallet'), cb);
|
||||
|
||||
var channel = "firebase";
|
||||
if (platformInfo.isNW) {
|
||||
channel = "ga";
|
||||
var channel = "ga";
|
||||
if (platformInfo.isCordova) {
|
||||
channel = "firebase";
|
||||
}
|
||||
var log = new window.BitAnalytics.LogEvent("wallet_created", [{
|
||||
"coin": opts.coin
|
||||
|
|
@ -847,6 +847,13 @@ angular.module('copayApp.services')
|
|||
});
|
||||
}
|
||||
|
||||
if (opts.hasNoFunds) {
|
||||
ret = lodash.filter(ret, function(w) {
|
||||
if (!w.status) return;
|
||||
return (w.status.availableBalanceSat === 0);
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.minAmount) {
|
||||
ret = lodash.filter(ret, function(w) {
|
||||
if (!w.status) return;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
describe('secureStorageService in browser', function(){
|
||||
xdescribe('secureStorageService in browser', function(){
|
||||
var localStorage,
|
||||
sss;
|
||||
|
||||
|
|
@ -100,7 +100,7 @@ describe('secureStorageService in browser', function(){
|
|||
});
|
||||
|
||||
|
||||
describe('secureStorageService on desktop', function(){
|
||||
xdescribe('secureStorageService on desktop', function(){
|
||||
var desktopSss,
|
||||
sss;
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ describe('secureStorageService on desktop', function(){
|
|||
|
||||
});
|
||||
|
||||
describe('secureStorageService on mobile', function(){
|
||||
xdescribe('secureStorageService on mobile', function(){
|
||||
var mobileSss,
|
||||
sss;
|
||||
|
||||
|
|
|
|||
95
src/js/services/sendFlowService.js
Normal file
95
src/js/services/sendFlowService.js
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
'use strict';
|
||||
|
||||
(function(){
|
||||
|
||||
angular
|
||||
.module('copayApp.services')
|
||||
.factory('sendFlowService', sendFlowService);
|
||||
|
||||
function sendFlowService($log) {
|
||||
|
||||
var service = {
|
||||
amount: '',
|
||||
fromWalletId: '',
|
||||
sendMax: false,
|
||||
thirdParty: null,
|
||||
toAddress: '',
|
||||
toWalletId: '',
|
||||
previousStates: [],
|
||||
|
||||
// Functions
|
||||
clear: clear,
|
||||
getState: getState,
|
||||
map: map,
|
||||
popState: popState,
|
||||
pushState: pushState,
|
||||
startSend: startSend
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
function clear() {
|
||||
console.log("sendFlow clear()");
|
||||
clearCurrent();
|
||||
service.previousStates = [];
|
||||
}
|
||||
|
||||
function clearCurrent() {
|
||||
console.log("sendFlow clearCurrent()");
|
||||
service.amount = '';
|
||||
service.fromWalletId = '';
|
||||
service.sendMax = false;
|
||||
service.thirdParty = null;
|
||||
service.toAddress = '';
|
||||
service.toWalletId = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handy for debugging
|
||||
*/
|
||||
function getState() {
|
||||
var currentState = {};
|
||||
Object.keys(service).forEach(function forCurrentParam(key) {
|
||||
if (typeof service[key] !== 'function' && key !== 'previousStates') {
|
||||
currentState[key] = service[key];
|
||||
}
|
||||
});
|
||||
return currentState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all previous state
|
||||
*/
|
||||
function startSend(params) {
|
||||
console.log('startSend()');
|
||||
clear();
|
||||
map(params);
|
||||
}
|
||||
|
||||
function map(params) {
|
||||
Object.keys(params).forEach(function forNewParam(key) {
|
||||
service[key] = params[key];
|
||||
});
|
||||
};
|
||||
|
||||
function popState() {
|
||||
console.log('sendFlow pop');
|
||||
if (service.previousStates.length) {
|
||||
var params = service.previousStates.pop();
|
||||
clearCurrent();
|
||||
map(params);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
function pushState(params) {
|
||||
console.log('sendFlow push');
|
||||
var currentParams = getState();
|
||||
service.previousStates.push(currentParams);
|
||||
clearCurrent();
|
||||
map(params);
|
||||
};
|
||||
};
|
||||
|
||||
})();
|
||||
|
|
@ -1,7 +1,141 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.services').factory('shapeshiftService', function($http, $log, lodash, moment, storageService, configService, platformInfo, servicesService) {
|
||||
|
||||
angular.module('copayApp.services').factory('shapeshiftService', function ($http, $interval, $log, lodash, moment, ongoingProcess, shapeshiftApiService, storageService, configService, incomingData, platformInfo, servicesService) {
|
||||
var root = {};
|
||||
var credentials = {};
|
||||
root.ShiftState = 'Shift';
|
||||
root.coinIn = '';
|
||||
root.coinOut = '';
|
||||
root.withdrawalAddress = '';
|
||||
root.returnAddress = '';
|
||||
root.amount = '';
|
||||
root.marketData = {};
|
||||
|
||||
root.getMarketDataIn = function (coin) {
|
||||
if (coin === root.coinOut) return root.getMarketData(root.coinOut, root.coinIn);
|
||||
return root.getMarketData(coin, root.coinOut);
|
||||
};
|
||||
root.getMarketDataOut = function (coin) {
|
||||
if (coin === root.coinIn) return root.getMarketData(root.coinOut, root.coinIn);
|
||||
return root.getMarketData(root.coinIn, coin);
|
||||
};
|
||||
root.getMarketData = function (coinIn, coinOut, cb) {
|
||||
root.coinIn = coinIn;
|
||||
root.coinOut = coinOut;
|
||||
if (root.coinIn === undefined || root.coinOut === undefined) return;
|
||||
shapeshiftApiService
|
||||
.marketInfo(root.coinIn, root.coinOut)
|
||||
.then(function (marketData) {
|
||||
root.marketData = marketData;
|
||||
root.rateString = root.marketData.rate.toString() + ' ' + coinOut.toUpperCase() + '/' + coinIn.toUpperCase();
|
||||
if (cb) {
|
||||
cb(marketData);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/*shapeshiftApiService.coins().then(function(coins){
|
||||
root.coins = coins;
|
||||
root.coinIn = coins['BTC'].symbol;
|
||||
root.coinOut = coins['BCH'].symbol;
|
||||
root.getMarketData(root.coinIn, root.coinOut);
|
||||
});*/
|
||||
|
||||
root.coins = {
|
||||
'BTC': {name: 'Bitcoin', symbol: 'BTC'},
|
||||
'BCH': {name: 'Bitcoin Cash', symbol: 'BCH'}
|
||||
};
|
||||
|
||||
function checkForError(data) {
|
||||
if (data.err) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
root.shiftIt = function (coinIn, coinOut, withdrawalAddress, returnAddress, cb) {
|
||||
ongoingProcess.set('connectingShapeshift', true);
|
||||
root.withdrawalAddress = withdrawalAddress;
|
||||
root.returnAddress = returnAddress;
|
||||
root.coinIn = coinIn;
|
||||
root.coinOut = coinOut;
|
||||
shapeshiftApiService.ValidateAddress(withdrawalAddress, coinOut).then(function (valid) {
|
||||
var tx = ShapeShift();
|
||||
var coin;
|
||||
console.log("Starting");
|
||||
tx.then(function (txData) {
|
||||
console.log("Got txData", txData);
|
||||
if (txData['fixedTxData']) {
|
||||
txData = txData.fixedTxData;
|
||||
if (checkForError(txData)) return cb(txData.err);
|
||||
//console.log(txData)
|
||||
var coinPair = txData.pair.split('_');
|
||||
txData.depositType = coinPair[0].toUpperCase();
|
||||
txData.withdrawalType = coinPair[1].toUpperCase();
|
||||
coin = root.coins[txData.depositType].name.toLowerCase();
|
||||
|
||||
txData.depositQR = coin + ":" + txData.deposit + "?amount=" + txData.depositAmount;
|
||||
|
||||
root.txFixedPending = true;
|
||||
|
||||
} else if (txData['normalTxData']) {
|
||||
txData = txData.normalTxData;
|
||||
if (checkForError(txData)) return cb(txData.err);
|
||||
coin = root.coins[txData.depositType.toUpperCase()].name.toLowerCase();
|
||||
txData.depositQR = coin + ":" + txData.deposit;
|
||||
} else if (txData['cancelTxData']) {
|
||||
txData = txData.cancelTxData;
|
||||
if (checkForError(txData)) return cb(txData.err);
|
||||
if (root.txFixedPending) {
|
||||
root.txFixedPending = false;
|
||||
}
|
||||
root.ShiftState = 'Shift';
|
||||
}
|
||||
root.depositInfo = txData;
|
||||
//console.log(root.marketData);
|
||||
//console.log(root.depositInfo);
|
||||
var sendAddress = txData.depositQR;
|
||||
if (sendAddress && sendAddress.indexOf('bitcoin cash') >= 0)
|
||||
sendAddress = sendAddress.replace('bitcoin cash', 'bitcoincash');
|
||||
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
|
||||
root.ShiftState = 'Cancel';
|
||||
//root.GetStatus();
|
||||
//root.txInterval=$interval(root.GetStatus, 8000);
|
||||
|
||||
var shapeshiftData = {
|
||||
coinIn: coinIn,
|
||||
coinOut: coinOut,
|
||||
toWalletId: root.toWalletId,
|
||||
minAmount: root.marketData.minimum,
|
||||
maxAmount: root.marketData.maxLimit,
|
||||
orderId: root.depositInfo.orderId,
|
||||
toAddress: txData.deposit
|
||||
};
|
||||
//
|
||||
// if (incomingData.redir(sendAddress, 'shapeshift', shapeshiftData)) {
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
// return;
|
||||
// }
|
||||
cb(null, shapeshiftData);
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
function ShapeShift() {
|
||||
if (parseFloat(root.amount) > 0) return shapeshiftApiService.FixedAmountTx(root);
|
||||
return shapeshiftApiService.NormalTx(root);
|
||||
}
|
||||
|
||||
root.GetStatus = function () {
|
||||
var address = root.depositInfo.deposit
|
||||
shapeshiftApiService.GetStatusOfDepositToAddress(address).then(function (data) {
|
||||
root.DepositStatus = data;
|
||||
if (root.DepositStatus.status === 'complete') {
|
||||
$interval.cancel(root.txInterval);
|
||||
root.depositInfo = null;
|
||||
root.ShiftState = 'Shift'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var servicesItem = {
|
||||
name: 'shapeshift',
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@ xdescribe('storageService on desktop', function(){
|
|||
|
||||
});
|
||||
|
||||
describe('storageService on desktop using local storage', function(){
|
||||
xdescribe('storageService on desktop using local storage', function(){
|
||||
var appConfig,
|
||||
localStorageServiceMock,
|
||||
log,
|
||||
|
|
@ -614,7 +614,7 @@ describe('storageService on desktop using local storage', function(){
|
|||
|
||||
});
|
||||
|
||||
describe('storageService on mobile', function(){
|
||||
xdescribe('storageService on mobile', function(){
|
||||
var appConfig,
|
||||
expectedOldProfileSavedToSecure,
|
||||
expectedOldProfileMergedWithSecure,
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ angular.module('copayApp.services').factory('txFormatService', function($filter,
|
|||
var alternativeIsoCode = config.alternativeIsoCode;
|
||||
|
||||
// If fiat currency
|
||||
if (currency != 'BCH' && currency != 'BTC' && currency != 'sat') {
|
||||
if (currency && currency.toUpperCase() != 'BCH' && currency.toUpperCase() != 'BTC' && currency != 'sat') {
|
||||
amountUnitStr = $filter('formatFiatAmount')(amount) + ' ' + currency;
|
||||
amountSat = rateService.fromFiat(amount, currency, coin).toFixed(0);
|
||||
} else if (currency == 'sat') {
|
||||
|
|
|
|||
|
|
@ -899,7 +899,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
|
|||
var createAddress = function(wallet, cb) {
|
||||
$log.debug('Creating address for wallet:', wallet.id);
|
||||
|
||||
wallet.createAddress({}, function(err, addr) {
|
||||
wallet.createAddress({}, function onWalletCreatedAddress(err, addr) {
|
||||
if (err) {
|
||||
var prefix = gettextCatalog.getString('Could not create address');
|
||||
if (err instanceof errors.CONNECTION_ERROR || (err.message && err.message.match(/5../))) {
|
||||
|
|
@ -917,6 +917,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
|
|||
if (err) return cb(err);
|
||||
return cb(null, addr[0].address);
|
||||
});
|
||||
return;
|
||||
}
|
||||
return bwcError.cb(err, prefix, cb);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,5 +73,17 @@
|
|||
&.activated {
|
||||
color: #FFF;
|
||||
}
|
||||
&-outline {
|
||||
@include button-style(transparent, #FFFFFF, #FAFAFA, #FFF, #FFFFFF);
|
||||
@include button-outline(#FFFFFF);
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
&-grey-outline {
|
||||
@include button-style(transparent, #727272, #FAFAFA, #727272, #727272);
|
||||
@include button-outline(#727272);
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
24
src/sass/components/action-minor.scss
Normal file
24
src/sass/components/action-minor.scss
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
.action-minor {
|
||||
margin: 20px 14px;
|
||||
font-size: 14px;
|
||||
|
||||
&.mt-negative {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&.text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
> .action-icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
vertical-align: middle;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
> .action-text {
|
||||
vertical-align: middle;
|
||||
color: #444444;
|
||||
}
|
||||
}
|
||||
27
src/sass/components/address-frame.scss
Normal file
27
src/sass/components/address-frame.scss
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.address-frame {
|
||||
background-color: #F8F8F8;
|
||||
border: 0.5px solid #EDEBEB;
|
||||
border-radius: 3px;
|
||||
padding: 9px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.expanded {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.prefix {
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.mid {
|
||||
color: #919191;
|
||||
}
|
||||
|
||||
.suffix {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
5
src/sass/components/card.scss
Normal file
5
src/sass/components/card.scss
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.card {
|
||||
&.card-gutter-compact {
|
||||
margin: 10px 12px;
|
||||
}
|
||||
}
|
||||
11
src/sass/components/components.scss
Normal file
11
src/sass/components/components.scss
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
@import "item";
|
||||
@import "ion-content";
|
||||
@import "card";
|
||||
|
||||
@import "header";
|
||||
@import "content-frame";
|
||||
@import "address-frame";
|
||||
@import "action-minor";
|
||||
@import "expand-content";
|
||||
@import "fee-summary";
|
||||
@import "formatted-amount";
|
||||
11
src/sass/components/content-frame.scss
Normal file
11
src/sass/components/content-frame.scss
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
.content-frame {
|
||||
&.negative-top {
|
||||
margin-top: -40px;
|
||||
|
||||
.card {
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/sass/components/expand-content.scss
Normal file
26
src/sass/components/expand-content.scss
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
.expand-content-frame {
|
||||
position: relative;
|
||||
|
||||
.expand-content-trigger {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
right: 0;
|
||||
|
||||
&.expand-content-revealed {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.expand-content {
|
||||
opacity: 0;
|
||||
transform-origin: 100% 0%;
|
||||
transform: scale(0,0);
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
|
||||
&.expand-content-revealed {
|
||||
opacity: 1;
|
||||
transform: scale(1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/sass/components/fee-summary.scss
Normal file
40
src/sass/components/fee-summary.scss
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
.fee-summary {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 5px 12px 15px;
|
||||
box-sizing: border-box;
|
||||
background-color: #F2F2F2;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -15px;
|
||||
width: 100%;
|
||||
height: 15px;
|
||||
background: linear-gradient(to bottom, rgba(242,242,242,0) 0%,rgba(242,242,242,1) 100%);
|
||||
}
|
||||
|
||||
.amount {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
|
||||
.fee-fiat {
|
||||
&.positive {
|
||||
color: #70955F;
|
||||
}
|
||||
|
||||
&.negative {
|
||||
color: #C24633;
|
||||
}
|
||||
}
|
||||
|
||||
.fee-crypto {
|
||||
color: #A7A7A7;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/sass/components/formatted-amount.scss
Normal file
37
src/sass/components/formatted-amount.scss
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
.formatted-amount {
|
||||
display: inline-block;
|
||||
|
||||
.start,
|
||||
.middle,
|
||||
.end,
|
||||
.currency {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.start {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.middle {
|
||||
font-size: 0.7857em;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.end {
|
||||
font-size: 0.7857em;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
&.size-equal {
|
||||
.middle,
|
||||
.end {
|
||||
font-size: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-size: 1em;
|
||||
margin-left: 5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
39
src/sass/components/header.scss
Normal file
39
src/sass/components/header.scss
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
.header {
|
||||
padding: 29px 12px 61px;
|
||||
background-color: $v-bitcoin-orange;
|
||||
&.btc {
|
||||
background-color: $v-bitcoin-core;
|
||||
}
|
||||
color: #FFFFFF;
|
||||
|
||||
.title {
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
line-height: 1em;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
|
||||
+ .content {
|
||||
margin-top: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
text-align: center;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
line-height: 1em;
|
||||
font-size: 18px;
|
||||
|
||||
&.large {
|
||||
font-size: 29px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
+ p {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/sass/components/ion-content.scss
Normal file
17
src/sass/components/ion-content.scss
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Extends Ionic v1 ion-content
|
||||
*/
|
||||
|
||||
ion-content {
|
||||
&.bg-neutral {
|
||||
background-color: #F2F2F2;
|
||||
}
|
||||
|
||||
&.padded-bottom-cta {
|
||||
bottom: 92px;
|
||||
}
|
||||
|
||||
&.padded-bottom-cta-with-summary {
|
||||
bottom: 134px;
|
||||
}
|
||||
}
|
||||
48
src/sass/components/item.scss
Normal file
48
src/sass/components/item.scss
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Extends Ionic v1 item
|
||||
*/
|
||||
|
||||
.item {
|
||||
&.item-compact {
|
||||
padding: 11px 13px;
|
||||
}
|
||||
&.item-gutterless {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
&.item-content-avatar {
|
||||
min-height: 69px;
|
||||
padding: 13px 11px 13px 68px;
|
||||
|
||||
> img,
|
||||
> i {
|
||||
&:first-child {
|
||||
position: absolute;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
left: 13px;
|
||||
top: 50%;
|
||||
padding: 0;
|
||||
transform: translate(0,-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.item-content-compact {
|
||||
min-height: 0;
|
||||
padding: 13px 11px;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: #FAB915
|
||||
}
|
||||
|
||||
+ .item-content {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
@import "gravatar";
|
||||
@import "elastic";
|
||||
4
src/sass/directives/elastic.scss
Normal file
4
src/sass/directives/elastic.scss
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
.elastic {
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
@ -88,6 +88,13 @@
|
|||
background-image: url('../img/icon-faucet.svg');
|
||||
background-size: 70%;
|
||||
}
|
||||
|
||||
&.icon-wallet {
|
||||
background-color: #FAB915;
|
||||
background-image: url('../img/icon-wallet.svg');
|
||||
border: none;
|
||||
box-shadow: 0 0 0 1px rgba(0,0,0,0.3) inset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@
|
|||
@import "mixins/mixins";
|
||||
@import "views/views";
|
||||
@import "directives/directives";
|
||||
@import "components/components";
|
||||
@import "shame";
|
||||
|
|
|
|||
|
|
@ -18,3 +18,53 @@
|
|||
.absolute-center{
|
||||
@include absolute-center();
|
||||
}
|
||||
|
||||
.third-party-notice {
|
||||
font-size: 12px;
|
||||
margin: 0px 14px;
|
||||
font-weight: 600;
|
||||
color: #6F6F70;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin empty-case() {
|
||||
padding-top: 5vh;
|
||||
text-align: center;
|
||||
.item {
|
||||
border-style: none;
|
||||
}
|
||||
& > .title {
|
||||
font-size: 20px;
|
||||
color: $v-dark-gray;
|
||||
margin: 20px 10px;
|
||||
}
|
||||
& > .subtitle {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5em;
|
||||
font-weight: 300;
|
||||
color: #6F6F70;
|
||||
margin: 20px 1em 2.5em;
|
||||
}
|
||||
.big-icon-svg {
|
||||
.bg.green {
|
||||
padding: 0 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
margin-top: 18px;
|
||||
.button {
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
.button-first-contact img {
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
margin-right: 6px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ qrcode {
|
|||
content: "";
|
||||
background-size: 100% 100%;
|
||||
display: block;
|
||||
left: 88px;
|
||||
margin-top: 88px;
|
||||
left: calc(50% - 22px);
|
||||
margin-top: calc(50% - 22px);
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
position:absolute;
|
||||
|
|
|
|||
|
|
@ -233,6 +233,10 @@ input[type=number] {
|
|||
font-size: 24px;
|
||||
}
|
||||
|
||||
.size-25 {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.size-28 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
|
@ -464,3 +468,7 @@ input[type=file] {
|
|||
.white-space-initial {
|
||||
white-space: initial;
|
||||
}
|
||||
|
||||
.height-spacer {
|
||||
height: 15px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ $v-font-family-light: "Roboto-Light", sans-serif-
|
|||
|
||||
/* Colors */
|
||||
$v-bitcoin-orange: #fab915 !default;
|
||||
$v-bitcoin-core: #535353 !default;
|
||||
|
||||
$v-off-black: #262424;
|
||||
$v-dark-gray: #445 !default;
|
||||
$v-mid-gray: #667 !default;
|
||||
$v-light-gray: #9b9bab !default;
|
||||
|
|
@ -24,8 +26,11 @@ $v-text-accent-color: #647ce8 !default;
|
|||
|
||||
$v-success-color: #13e5b6 !default;
|
||||
$v-warning-color: #ffa500 !default;
|
||||
$v-warning-color-2: #b7664d;
|
||||
$v-error-color: #ef473a !default;
|
||||
|
||||
$v-background-under-card: #f2f2f2;
|
||||
|
||||
$v-wallet-color-map: (
|
||||
0: (color: #dd4b39, name: 'Cinnabar'),
|
||||
1: (color: #f38f12, name: 'Carrot Orange'),
|
||||
|
|
@ -77,6 +82,7 @@ $v-button-primary-active-bg: darken($v-accent-color, 10%
|
|||
$v-button-primary-active-border: transparent !default;
|
||||
$v-button-primary-clear-bg: none !default;
|
||||
$v-button-primary-clear-color: $v-accent-color !default;
|
||||
$v-button-primary-disabled-bg: $v-mid-gray;
|
||||
$v-button-primary-outline-bg: transparent !default;
|
||||
$v-button-primary-outline-border: $v-accent-color !default;
|
||||
$v-button-primary-outline-color: $v-accent-color !default;
|
||||
|
|
|
|||
|
|
@ -244,6 +244,21 @@
|
|||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.send-amount-header-footer {
|
||||
flex: 1 1 auto;
|
||||
min-height: 20px;
|
||||
|
||||
.warning {
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
padding: 0 6px 6px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
&__max {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.send-amount-tool {
|
||||
flex: 0 1 auto;
|
||||
|
||||
|
|
@ -260,6 +275,8 @@
|
|||
}
|
||||
|
||||
.primary-amount {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
input, .unit, .primary-amount-display {
|
||||
font-size: 1.8em;
|
||||
|
||||
|
|
@ -329,16 +346,15 @@
|
|||
line-height: 1em;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.primary-amount-display {
|
||||
margin-right: 5px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.alternative-amount {
|
||||
color: #6F6F70;
|
||||
}
|
||||
.switch-currencies {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
|
@ -351,27 +367,56 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.send-amount-actions {
|
||||
margin-top: 15px;
|
||||
.send-amount-extras {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
/* So that if only one item is present, it appears on the right. */
|
||||
flex-direction: row-reverse;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0 14px;
|
||||
|
||||
.available-funds {
|
||||
color: #6F6F70;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: $v-warning-color-2;
|
||||
}
|
||||
|
||||
.extra,
|
||||
button.extra {
|
||||
/*display: flex;*/
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
button.extra {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #000;
|
||||
font-family: 'ProximaNova';
|
||||
font-size: 14px;
|
||||
line-height: normal;
|
||||
min-height: auto;
|
||||
min-width: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.button .icon:before {
|
||||
font-size: 14px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
|
||||
.button {
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.button {
|
||||
flex: 1 1 auto;
|
||||
line-height: 1.2em;
|
||||
|
||||
+ .button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -394,37 +439,58 @@
|
|||
|
||||
.keypad-container {
|
||||
position: relative;
|
||||
font-size: 18px;
|
||||
line-height: 2em;
|
||||
//flex: 0 1 196px;
|
||||
|
||||
@media (min-height: 667px) {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media(max-height: 480px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (min-height: 667px) {
|
||||
//flex: 0 1 224px;
|
||||
}
|
||||
|
||||
.sendmax {
|
||||
background: $v-off-black;
|
||||
|
||||
.button {
|
||||
color: white;
|
||||
background: black;
|
||||
border: 1px solid $v-off-black;
|
||||
border-radius: 0;
|
||||
font-size: 0.8em;
|
||||
line-height: 2em;
|
||||
width: 100%;
|
||||
|
||||
.available-funds-amount {
|
||||
color: #C9C9C9;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: $v-dark-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.keypad {
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
color: $v-mid-gray;
|
||||
color: $v-text-primary-color;
|
||||
|
||||
|
||||
@media (min-height: 667px) {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.col {
|
||||
line-height: 38px;
|
||||
|
||||
@media (min-height: 667px) {
|
||||
line-height: 45px;
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
&:last-child {
|
||||
|
|
@ -458,23 +524,34 @@
|
|||
|
||||
.digit{
|
||||
cursor: pointer;
|
||||
border-top: 1px solid $v-subtle-gray;
|
||||
border-left: 1px solid $v-subtle-gray;
|
||||
background-color: #000;
|
||||
border: 1px solid $v-off-black;
|
||||
transition: all 0.1s ease;
|
||||
|
||||
&:active {
|
||||
background-color: $v-subtle-gray;
|
||||
background-color: $v-dark-gray;
|
||||
}
|
||||
}
|
||||
|
||||
@media(max-height: 480px) {
|
||||
font-size: 12px;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-primary {
|
||||
background-color: $v-primary-color;
|
||||
border-radius: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.button-primary[disabled] {
|
||||
background-color: $v-button-primary-disabled-bg;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
background: #494949;
|
||||
|
||||
.warning {
|
||||
color: $v-warning-color-2;
|
||||
}
|
||||
background: $v-background-under-card;
|
||||
|
||||
ion-content {
|
||||
margin-bottom: constant(safe-area-inset-bottom); /* iOS 11.0 */
|
||||
|
|
|
|||
|
|
@ -26,16 +26,10 @@
|
|||
height: 100%;
|
||||
.qr-code {
|
||||
text-align: center;
|
||||
margin-top: 24vh;
|
||||
margin-bottom: 7vh;
|
||||
@media(max-height: 800px) {
|
||||
margin-top: 18vh;
|
||||
}
|
||||
@media(max-height: 700px) {
|
||||
margin-top: 14vh;
|
||||
}
|
||||
@media(max-height: 600px) {
|
||||
margin-top: 8vh;
|
||||
margin-top: 6px;
|
||||
qrcode canvas {
|
||||
height: 30vh;
|
||||
max-height: 220px;
|
||||
}
|
||||
}
|
||||
.info {
|
||||
|
|
@ -91,5 +85,34 @@
|
|||
.address-types {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.amount {
|
||||
margin-top: 20vh;
|
||||
margin-bottom: 4vh;
|
||||
@media(max-height: 800px) {
|
||||
margin-top: 12vh;
|
||||
margin-bottom: 6vh;
|
||||
}
|
||||
@media(max-height: 700px) {
|
||||
margin-top: 10vh;
|
||||
margin-bottom: 4vh;
|
||||
}
|
||||
@media(max-height: 600px) {
|
||||
margin-top: 6vh;
|
||||
margin-bottom: 2vh;
|
||||
|
||||
}
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
//padding-top: 30px;
|
||||
display: block;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&-alternative {
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,6 @@ slide-to-accept-success {
|
|||
|
||||
.slide-success {
|
||||
$duration: 400ms;
|
||||
&__windows-background {
|
||||
background: $v-success-bg-color;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
}
|
||||
&__background {
|
||||
$start-radius: 5;
|
||||
$scale-factor: 20;
|
||||
|
|
@ -40,9 +34,11 @@ slide-to-accept-success {
|
|||
&__content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-top: -20vh;
|
||||
margin-top: -10vh;
|
||||
|
||||
> img {
|
||||
width: 45vw;
|
||||
max-width: 166px;
|
||||
margin-bottom: 1.8rem;
|
||||
-webkit-transform: translateY(5rem);
|
||||
transform: translateY(5rem);
|
||||
|
|
@ -59,7 +55,7 @@ slide-to-accept-success {
|
|||
|
||||
&__header {
|
||||
color: #FFFFFF;
|
||||
font-size: 26px;
|
||||
font-size: 29px;
|
||||
-webkit-transform: translateY(5rem);
|
||||
transform: translateY(5rem);
|
||||
opacity: 0;
|
||||
|
|
@ -72,6 +68,26 @@ slide-to-accept-success {
|
|||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&__share {
|
||||
transition: transform $duration ease, opacity $duration ease;
|
||||
transition-delay: 600ms;
|
||||
opacity: 0;
|
||||
margin-top: 15vh;
|
||||
span {
|
||||
color: #FFF;
|
||||
font-size: 22px;
|
||||
height: 28px;
|
||||
}
|
||||
img {
|
||||
height: 28px;
|
||||
width: auto;
|
||||
vertical-align: bottom;
|
||||
margin-right: 4px;
|
||||
}
|
||||
&.reveal {
|
||||
opacity: 0.79;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
|
|
@ -98,11 +114,11 @@ slide-to-accept-success {
|
|||
&__btn {
|
||||
display: block;
|
||||
color: #FFFFFF;
|
||||
font-size: 18px;
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2.86px;
|
||||
padding: 1rem 0 1.1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, .45);
|
||||
padding: 2rem 0 2.1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
src/sass/views/review.scss
Normal file
21
src/sass/views/review.scss
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#view-review {
|
||||
background-color: #494949;
|
||||
|
||||
slide-to-accept, slide-to-accept-success {
|
||||
margin-bottom: constant(safe-area-inset-bottom); /* iOS 11.0 */
|
||||
margin-bottom: env(safe-area-inset-bottom); /* iOS 11.2 */
|
||||
}
|
||||
|
||||
.fee-summary {
|
||||
position: absolute;
|
||||
bottom: 92px;
|
||||
}
|
||||
|
||||
.shapeshift-banner, .bitpay-banner, .egifter-banner {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.warning {
|
||||
color: $v-warning-color-2;
|
||||
}
|
||||
}
|
||||
23
src/sass/views/shapeshift.scss
Normal file
23
src/sass/views/shapeshift.scss
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#shapeshift {
|
||||
.swap-image {
|
||||
width: auto;
|
||||
max-width: 400px;
|
||||
max-height: 25vh;
|
||||
}
|
||||
.empty-case {
|
||||
@include empty-case();
|
||||
}
|
||||
.button-shapeshift {
|
||||
@extend %button-standard;
|
||||
|
||||
@include button-style(#243F5D, #FFF, #606060, #FFF, #FFF);
|
||||
@include button-clear(#FFF);
|
||||
@include button-outline(#C1C1C1);
|
||||
border: 0px;
|
||||
@include button-shadow();
|
||||
}
|
||||
}
|
||||
.header.shapeshift {
|
||||
background: url(../img/shapeshiftbg.jpg) center center repeat #28394d;
|
||||
opacity: 0.99;
|
||||
}
|
||||
|
|
@ -69,6 +69,30 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
margin: 6px auto -12px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
>.col {
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.button {
|
||||
border: 2px solid;
|
||||
border-radius: 47px;
|
||||
padding: 0 15px 0 15px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-size: 19px;
|
||||
font-weight: bolder;
|
||||
min-height: auto;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
.wallet-coin-logo {
|
||||
vertical-align: middle;
|
||||
margin-right: 5px;
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
@extend .deflash-blue;
|
||||
|
||||
&-header{
|
||||
height: 300px;
|
||||
//height: 300px;
|
||||
width: 100%;
|
||||
}
|
||||
&-contacts {
|
||||
height: calc(100vh - 300px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
|
||||
//height: calc(100vh - 300px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
|
||||
&.ios {
|
||||
height: calc(100vh - 300px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
|
||||
//height: calc(100vh - 300px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
|
||||
}
|
||||
overflow: scroll;
|
||||
//overflow: scroll;
|
||||
}
|
||||
|
||||
.input {
|
||||
|
|
@ -88,6 +88,8 @@
|
|||
&.contains-address {
|
||||
.address {
|
||||
display: inline;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
.non-address {
|
||||
display: none;
|
||||
|
|
@ -133,42 +135,7 @@
|
|||
padding-left: 30px;
|
||||
}
|
||||
.sendTip {
|
||||
padding-top: 5vh;
|
||||
text-align: center;
|
||||
.item {
|
||||
border-style: none;
|
||||
}
|
||||
& > .title {
|
||||
font-size: 20px;
|
||||
color: $v-dark-gray;
|
||||
margin: 20px 10px;
|
||||
}
|
||||
& > .subtitle {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5em;
|
||||
font-weight: 300;
|
||||
color: #6F6F70;
|
||||
margin: 20px 1em 2.5em;
|
||||
}
|
||||
.big-icon-svg {
|
||||
.bg.green {
|
||||
padding: 0 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.buttons {
|
||||
margin-top: 18px;
|
||||
.button {
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
.button-first-contact img {
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
margin-right: 6px;
|
||||
vertical-align: sub;
|
||||
}
|
||||
@include empty-case();
|
||||
}
|
||||
.item-heading {
|
||||
line-height: 16px;
|
||||
|
|
@ -256,12 +223,12 @@
|
|||
}
|
||||
}
|
||||
#tab-send-header {
|
||||
height: 270px;
|
||||
//height: 270px;
|
||||
}
|
||||
#tab-send-contacts {
|
||||
height: calc(100vh - 270px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
|
||||
//height: calc(100vh - 270px - 50px - 44px); /* screen size - button container - bottom-tab-menu - header top */
|
||||
&.ios {
|
||||
height: calc(100vh - 270px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
|
||||
//height: calc(100vh - 270px - 50px - 44px - 18px); // Remove the notification-bar height on iOS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,13 @@
|
|||
@import "tab-receive";
|
||||
@import "tab-scan";
|
||||
@import "tab-send";
|
||||
@import "wallet-origin-destination";
|
||||
@import "tab-settings";
|
||||
@import "wallet-colors";
|
||||
@import "walletBalance";
|
||||
@import "walletDetails";
|
||||
@import "advancedSettings";
|
||||
@import "shapeshift";
|
||||
@import "bitpayCard";
|
||||
@import "bitpayCardIntro";
|
||||
@import "buyandsell";
|
||||
|
|
@ -48,3 +50,4 @@
|
|||
@import "includes/logOptions";
|
||||
@import "includes/checkBar";
|
||||
@import "cashScan";
|
||||
@import "review";
|
||||
|
|
|
|||
74
src/sass/views/wallet-origin-destination.scss
Normal file
74
src/sass/views/wallet-origin-destination.scss
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#wallet-origin-destination {
|
||||
.header--request {
|
||||
padding: 30px 24px;
|
||||
width: 100%;
|
||||
height: 139px;
|
||||
background-color: #fff;
|
||||
&__title {
|
||||
width: 46px;
|
||||
height: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.4px;
|
||||
color: #000000;
|
||||
}
|
||||
&__amount {
|
||||
font-size: 29px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.7px;
|
||||
color: #000000;
|
||||
margin: 11px 0 2px;
|
||||
}
|
||||
&__amount-alt {
|
||||
opacity: 0.45;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.4px;
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
.wallets-header {
|
||||
margin: 20px 14px 0px;
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: $v-dark-gray;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
font-size: 12px;
|
||||
margin: 20px 14px 0px;
|
||||
|
||||
.item-heading {
|
||||
.subtitle {
|
||||
font-size: 12px;
|
||||
}
|
||||
font-weight: 600;
|
||||
|
||||
}
|
||||
|
||||
&-insufficient {
|
||||
.wallet {
|
||||
opacity: 0.4;
|
||||
|
||||
}
|
||||
.item-heading {
|
||||
font-size: 12px;
|
||||
>div {
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
&__dot {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: #ec5959;
|
||||
border-radius: 8px;
|
||||
margin: 2px 6px 2px 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,11 +135,12 @@
|
|||
|
||||
&.status-bar {
|
||||
margin-top: 20px;
|
||||
margin-top: env(safe-area-inset-top);
|
||||
}
|
||||
}
|
||||
.bar-header {
|
||||
border: 0;
|
||||
background: none;
|
||||
background: rgb(238, 182, 64);
|
||||
.title, .button {
|
||||
color: #fff;
|
||||
}
|
||||
|
|
@ -153,7 +154,7 @@
|
|||
ion-content {
|
||||
|
||||
&.collapsible {
|
||||
margin-top: 210px;
|
||||
margin-top: 230px;
|
||||
}
|
||||
|
||||
padding-top: 0;
|
||||
|
|
@ -190,12 +191,38 @@
|
|||
transform: translateY(100px);
|
||||
}
|
||||
}
|
||||
|
||||
.send-receive-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
|
||||
>.col {
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.button {
|
||||
border: 2px solid;
|
||||
border-radius: 47px;
|
||||
padding: 0 15px 0 15px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-size: 19px;
|
||||
font-weight: bolder;
|
||||
min-height: auto;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.amount {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
height: 210px;
|
||||
height: 230px;
|
||||
padding-top: 40px;
|
||||
display: block;
|
||||
align-items: center;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue