diff --git a/src/js/controllers/amount.js b/src/js/controllers/amount.js index 73ebfd150..c2dcc6cd4 100644 --- a/src/js/controllers/amount.js +++ b/src/js/controllers/amount.js @@ -29,6 +29,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, vm.finish = finish; vm.goBack = goBack; vm.loadMore = loadMore; + vm.next = next; vm.openPopup = openPopup; vm.pushDigit = pushDigit; vm.removeDigit = removeDigit; @@ -66,15 +67,14 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, } function onBeforeEnter(event, data) { - console.log('amount onBeforeEnter sendflow ', sendFlowService.getState()); - if (data.direction == "back") { sendFlowService.popState(); } + console.log('amount onBeforeEnter after back sendflow ', sendFlowService.state); initCurrencies(); - passthroughParams = sendFlowService; + passthroughParams = sendFlowService.getStateClone(); vm.fromWalletId = passthroughParams.fromWalletId; vm.toWalletId = passthroughParams.toWalletId; @@ -463,7 +463,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, }; if (vm.thirdParty) { - confirmData['thirdParty'] = this.thirdParty; + confirmData.thirdParty = vm.thirdParty; } sendFlowService.pushState(confirmData); @@ -478,7 +478,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, // Currency - var next = 10; + var nextCurrencies = 10; var completeAlternativeList = []; var popularCurrencyList = [ @@ -537,12 +537,17 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory, function loadMore() { $timeout(function() { - vm.altCurrencyList = completeAlternativeList.slice(0, next); - next += 10; + vm.altCurrencyList = completeAlternativeList.slice(0, nextCurrencies); + nextCurrencies += 10; vm.listComplete = vm.altCurrencyList.length >= completeAlternativeList.length; $scope.$broadcast('scroll.infiniteScrollComplete'); }, 100); - }; + } + + function next() { + useSendMax = false; + vm.finish(); + } function findCurrency(search) { if (!search) initCurrencies(); diff --git a/src/js/controllers/review.controller.js b/src/js/controllers/review.controller.js index a3b015348..3c528c1f9 100644 --- a/src/js/controllers/review.controller.js +++ b/src/js/controllers/review.controller.js @@ -75,9 +75,9 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit function onBeforeEnter(event, data) { - console.log('walletSelector onBeforeEnter sendflow ', sendFlowService.getState()); + console.log('walletSelector onBeforeEnter sendflow ', sendFlowService.state); defaults = configService.getDefaults(); - sendFlowData = sendFlowService.getState(); + sendFlowData = sendFlowService.getStateClone(); originWalletId = sendFlowData.fromWalletId; satoshis = parseInt(sendFlowData.amount, 10); toAddress = sendFlowData.toAddress; @@ -116,7 +116,9 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit if (!tx || !vm.originWallet) return; if (vm.paymentExpired) { - popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.')); + popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function () { + $ionicHistory.goBack(); + })); vm.sendStatus = ''; $timeout(function() { $scope.$apply(); @@ -223,7 +225,7 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit // Grab stateParams tx = { amount: parseInt(sendFlowData.amount), - sendMax: sendFlowData.sendMax === 'true' ? true : false, + sendMax: sendFlowData.sendMax, fromWalletId: sendFlowData.fromWalletId, toAddress: sendFlowData.toAddress, paypro: txPayproData, @@ -486,20 +488,26 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit walletService.getAddress(vm.originWallet, false, function onReturnWalletAddress(err, returnAddr) { if (err) { ongoingProcess.set('connectingShapeshift', false); - popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString()); + popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function () { + $ionicHistory.goBack(); + }); return; } walletService.getAddress(toWallet, false, function onWithdrawalWalletAddress(err, withdrawalAddr) { if (err) { ongoingProcess.set('connectingShapeshift', false); - popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString()); + popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function () { + $ionicHistory.goBack(); + }); 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()); + popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function () { + $ionicHistory.goBack(); + }); } else { vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId; vm.memoExpanded = !!vm.memo; @@ -640,7 +648,9 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit $timeout(function() { $scope.$apply(); }); - popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg)); + popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function () { + $ionicHistory.goBack(); + }); }; function setupTx(tx) { @@ -825,7 +835,9 @@ function reviewController(addressbookService, bitcoinCashJsService, bitcore, bit 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')); + popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function () { + $ionicHistory.goBack(); + }); return cb('no_funds'); } diff --git a/src/js/controllers/tab-home.js b/src/js/controllers/tab-home.js index 51d500de7..318fcece2 100644 --- a/src/js/controllers/tab-home.js +++ b/src/js/controllers/tab-home.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.controllers').controller('tabHomeController', - 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) { + function($rootScope, $timeout, $scope, $state, $stateParams, $ionicModal, $ionicScrollDelegate, $window, gettextCatalog, lodash, popupService, ongoingProcess, bannerService, communityService, externalLinkService, latestReleaseService, profileService, walletService, configService, $log, platformInfo, sendFlowService, storageService, txpModalService, appConfigService, startupService, addressbookService, bwcError, nextStepsService, buyAndSellService, homeIntegrationsService, bitpayCardService, pushNotificationsService, timeService, bitcoincomService, pricechartService, firebaseEventsService, servicesService, shapeshiftService, $ionicNavBarDelegate, signVerifyMessageService) { var wallet; var listeners = []; var notifications = []; @@ -58,7 +58,7 @@ angular.module('copayApp.controllers').controller('tabHomeController', }); } }; - + function onEnter(event, data) { $ionicNavBarDelegate.showBar(true); updateAllWallets(); @@ -214,6 +214,7 @@ angular.module('copayApp.controllers').controller('tabHomeController', var j = 0; lodash.each(wallets, function(wallet) { + walletService.invalidateCache(wallet); // Temporary solution, to have the good balance, when we ask to reload the wallets. walletService.getStatus(wallet, {}, function(err, status) { if (err) { diff --git a/src/js/controllers/tab-receive.js b/src/js/controllers/tab-receive.js index 190f1b752..0e80f4382 100644 --- a/src/js/controllers/tab-receive.js +++ b/src/js/controllers/tab-receive.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError, bitcoinCashJsService, $ionicNavBarDelegate, txFormatService, soundService, clipboardService) { +angular.module('copayApp.controllers').controller('tabReceiveController', function($rootScope, $scope, $timeout, $log, $ionicModal, $state, $ionicHistory, $ionicPopover, storageService, platformInfo, walletService, profileService, configService, lodash, gettextCatalog, popupService, bwcError, bitcoinCashJsService, $ionicNavBarDelegate, sendFlowService, txFormatService, soundService, clipboardService) { var listeners = []; $scope.bchAddressType = { type: 'cashaddr' }; @@ -18,9 +18,10 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi $scope.displayBalanceAsFiat = true; $scope.requestSpecificAmount = function() { - $state.go('tabs.paymentRequest.amount', { + sendFlowService.pushState({ toWalletId: $scope.wallet.credentials.walletId }); + $state.go('tabs.paymentRequest.amount'); }; $scope.setAddress = function(newAddr, copyAddress) { @@ -158,6 +159,10 @@ angular.module('copayApp.controllers').controller('tabReceiveController', functi soundService.play('misc/payment_received.mp3'); } + // Notify new tx + $scope.$emit('bwsEvent', $scope.wallet.id); + + $scope.$apply(function () { $scope.showingPaymentReceived = true; }); diff --git a/src/js/controllers/tab-send.js b/src/js/controllers/tab-send.js index 81b3e4ca5..9ac6c35cb 100644 --- a/src/js/controllers/tab-send.js +++ b/src/js/controllers/tab-send.js @@ -28,14 +28,15 @@ angular.module('copayApp.controllers').controller('tabSendController', function( }; $scope.$on("$ionicView.enter", function(event, data) { + + var stateParams = sendFlowService.getStateClone(); + $scope.fromWallet = profileService.getWallet(stateParams.fromWalletId); + clipboardService.readFromClipboard(function(text) { if (text.length > 200) { 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 @@ -183,7 +184,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function( $log.debug('Got toAddress:' + toAddress + ' | ' + item.name); - var stateParams = sendFlowService.getState(); + var stateParams = sendFlowService.getStateClone(); stateParams.toAddress = toAddress, stateParams.coin = item.coin; sendFlowService.pushState(stateParams); @@ -199,7 +200,7 @@ angular.module('copayApp.controllers').controller('tabSendController', function( $scope.startWalletToWalletTransfer = function() { console.log('startWalletToWalletTransfer()'); - var params = sendFlowService.getState(); + var params = sendFlowService.getStateClone(); sendFlowService.pushState(params); $state.transitionTo('tabs.send.wallet-to-wallet', { fromWalletId: sendFlowService.fromWalletId @@ -221,7 +222,7 @@ 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()); + console.log('tab-send onBeforeEnter sendflow ', sendFlowService.state); $scope.isIOS = platformInfo.isIOS && platformInfo.isCordova; $scope.showWalletsBch = $scope.showWalletsBtc = $scope.showWallets = false; diff --git a/src/js/controllers/walletDetails.js b/src/js/controllers/walletDetails.js index e6a8e2351..1aaef7581 100644 --- a/src/js/controllers/walletDetails.js +++ b/src/js/controllers/walletDetails.js @@ -304,6 +304,7 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun }; function refreshAmountSection(scrollPos) { + var AMOUNT_HEIGHT_BASE = 270; $scope.showBalanceButton = false; if ($scope.status) { $scope.showBalanceButton = ($scope.status.totalBalanceSat != $scope.status.spendableAmount); @@ -315,16 +316,16 @@ angular.module('copayApp.controllers').controller('walletDetailsController', fun } scrollPos = scrollPos || 0; - var amountHeight = 210 - scrollPos; + var amountHeight = AMOUNT_HEIGHT_BASE - scrollPos; if (amountHeight < 80) { amountHeight = 80; } var contentMargin = amountHeight; - if (contentMargin > 210) { - contentMargin = 210; + if (contentMargin > AMOUNT_HEIGHT_BASE) { + contentMargin = AMOUNT_HEIGHT_BASE; } - var amountScale = (amountHeight / 210); + var amountScale = (amountHeight / AMOUNT_HEIGHT_BASE); if (amountScale < 0.5) { amountScale = 0.5; } diff --git a/src/js/controllers/walletSelectorController.js b/src/js/controllers/walletSelectorController.js index 74ed874d4..7a7c5a463 100644 --- a/src/js/controllers/walletSelectorController.js +++ b/src/js/controllers/walletSelectorController.js @@ -11,13 +11,12 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu $scope.$on("$ionicView.enter", onEnter); function onBeforeEnter(event, data) { - console.log('walletSelector onBeforeEnter sendflow', sendFlowService.getState()); - if (data.direction == "back") { sendFlowService.popState(); } + console.log('walletSelector onBeforeEnter after back sendflow', sendFlowService.state); - var stateParams = sendFlowService.getState(); + $scope.params = sendFlowService.getStateClone(); var config = configService.getSync().wallet.settings; priceDisplayAsFiat = config.priceDisplay === 'fiat'; @@ -29,21 +28,23 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu $scope.sendFlowTitle = gettextCatalog.getString('Wallet to Wallet Transfer'); break; case 'tabs.send.destination': - if (stateParams.fromWalletId) { + if ($scope.params.fromWalletId && !$scope.params.thirdParty) { $scope.sendFlowTitle = gettextCatalog.getString('Wallet to Wallet Transfer'); } break; default: + if (!$scope.params.thirdParty) { + $scope.sendFlowTitle = gettextCatalog.getString('Send'); + } // 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()); + $state.transitionTo(getNextStep($scope.params)); } if ($scope.params.coin) { @@ -103,13 +104,10 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu } } - function getNextStep() { - if ($scope.thirdParty) { - $scope.params.thirdParty = $scope.thirdParty - } - if (!$scope.params.toWalletId && !$scope.params.toAddress) { // If we have no toAddress or fromWallet + function getNextStep(params) { + if (!params.toWalletId && !params.toAddress) { // If we have no toAddress or fromWallet return 'tabs.send.destination'; - } else if (!$scope.params.amount) { // If we have no amount + } else if (!params.amount) { // If we have no amount return 'tabs.send.amount'; } else { // If we do have them return 'tabs.send.review'; @@ -193,14 +191,16 @@ angular.module('copayApp.controllers').controller('walletSelectorController', fu $scope.useWallet = function(wallet) { - var params = sendFlowService.getState(); + var params = sendFlowService.getStateClone(); 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); + var nextStep = getNextStep(params); + console.log('walletSelector nextStep', nextStep); + $state.transitionTo(nextStep, $scope.params); }; $scope.goBack = function() { diff --git a/src/js/directives/formattedAmount.js b/src/js/directives/formattedAmount.js index c0c341631..f81615593 100644 --- a/src/js/directives/formattedAmount.js +++ b/src/js/directives/formattedAmount.js @@ -1,5 +1,6 @@ 'use strict'; +(function(){ /** * @desc amount directive that can be used to display formatted financial values * size-equal attribute is optional, defaults to false. @@ -10,168 +11,189 @@ * @example * @example */ -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; - } - }); + angular + .module('bitcoincom.directives') + .directive('formattedAmount', function() { + return { + restrict: 'E', + scope: { + value: '@', + currency: '@', + sizeEqual: '@' + }, + templateUrl: 'views/includes/formatted-amount.html', + controller: formattedAmountController } + }); + + function formattedAmountController($scope, uxLanguage) { + $scope.vm = {}; + var vm = $scope.vm; + + vm.currency = ''; + vm.value = ''; + + $scope.canShow = false + $scope.displaySizeEqual = !!$scope.sizeEqual; + + 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'] + }; + + $scope.$watch('value', function onFormattedAmountWatch() { + formatNumbers(); + }); + + function buildAmount(start, middle, end) { + $scope.start = start; + $scope.middle = middle; + $scope.end = end; + }; + + /** + * On Android 4.4, toLocaleString() only returns 3 fractional digits when 8 is specified. + */ + function ensureEnoughFractionDigits(localizedString, number, desiredFractionDigits) { + if (desiredFractionDigits === 0) { + // Assume it is OK + return localizedString; + } + var fractionalRe = /^-*(\d*\D)(\d+)$/; + var match = fractionalRe.exec(localizedString); + if (!match || 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 || 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; + } + + function formatNumbers() { + // Might get "< 0.01 USD" being passed in. + // 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.. + if (!$scope.currency || $scope.currency.length === 0) { + var currencySplit = $scope.value.split(" "); + if (currencySplit.length >= 2) { + vm.currency = currencySplit[currencySplit.length - 1]; + } + } else { + vm.currency = $scope.currency; + } + + // Redo this when we have proper formatting for low fees + if ($scope.value.indexOf("<") === 0) { + buildAmount($scope.value, '', ''); + vm.currency = ''; + $scope.canShow = true; + return; + } + + // Remove thousands separators for parseFloat() + $scope.value = $scope.value.replace(',', ''); + + var parsed = parseFloat($scope.value); + var valueFormatted = ''; + var valueProcessing = ''; + switch (getDecimalPlaces(vm.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; + $scope.$apply(); + }; + + function getDecimalPlaces(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'; + }; + + function getDecimalSeparator() { + 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; + }; + + function localizeNumbers(x, minimumFractionDigits) { + var parsed = parseFloat(x); + var opts = { + minimumFractionDigits: minimumFractionDigits, + useGrouping: true + }; + var lang = uxLanguage.getCurrentLanguage(); + var localized = parsed.toLocaleString(lang, opts); + var corrected = ensureEnoughFractionDigits(localized, x, minimumFractionDigits); + return corrected; }; } -); \ No newline at end of file + +})(); \ No newline at end of file diff --git a/src/js/directives/walletBalanceDirective.js b/src/js/directives/walletBalanceDirective.js new file mode 100644 index 000000000..1fea59203 --- /dev/null +++ b/src/js/directives/walletBalanceDirective.js @@ -0,0 +1,120 @@ +'use strict'; + +(function(){ + + angular + .module('bitcoincom.directives') + .directive('walletBalance', function() { + return { + restrict: 'E', + scope: { + displayAsFiat: '@', + totalBalanceSat: '@', + // The Wallet object is sometimes not stringify()-able, so not interpolatable, + // so can't be passed to a directive. + walletStatus: '@', + walletCachedBalance: '@', + walletCachedBalanceUpdatedOn: '@', + walletCachedStatus: '@' + }, + templateUrl: 'views/includes/wallet-balance.html', + controller: walletBalanceController + } + }); + + function walletBalanceController($log, $scope, txFormatService) { + var cryptoBalanceHasBeenDisplayed = false; + + formatBalance(); + $scope.$watchGroup(['displayAsFiat', 'totalBalanceSat'], function onWalletBalanceWatch() { + formatBalance(); + }); + + function displayCryptoBalance(walletStatus, walletCachedBalance, walletCachedBalanceUpdatedOn, walletCachedStatus) { + console.log('displayCryptoBalance()'); + + if (walletStatus && walletStatus.isValid && walletStatus.totalBalanceStr) { + setDisplay(walletStatus.totalBalanceStr, ''); + cryptoBalanceHasBeenDisplayed = true; + return; + } + + if (walletCachedBalance) { + setDisplay(walletCachedBalance, walletCachedBalanceUpdatedOn); + return; + } + + if (walletCachedStatus && walletCachedStatus.isValid && walletCachedStatus.totalBalanceStr) { + setDisplay(walletCachedStatus.totalBalanceStr, ''); + return; + } + + setDisplay('', ''); + } + + function displayFiatBalance(walletStatus, walletCachedStatus) { + var displayAmount = ''; + if (walletStatus && walletStatus.isValid && walletStatus.alternativeBalanceAvailable) { + displayAmount = walletStatus.totalBalanceAlternative + ' ' + walletStatus.alternativeIsoCode; + setDisplay(displayAmount, ''); + return; + } + + if (walletCachedStatus && walletCachedStatus.isValid && walletCachedStatus.alternativeBalanceAvailable) { + displayAmount = walletCachedStatus.totalBalanceAlternative + ' ' + walletCachedStatus.alternativeIsoCode; + setDisplay(displayAmount, ''); + return; + } + + getFiatBalance(wallet); + } + + function formatBalance() { + var displayAsFiat = $scope.displayAsFiat === 'true'; + + var walletStatusObj = null; + var walletCachedBalance = null; + var walletCachedBalanceUpdatedOn = null; + var walletCachedStatusObj = null; + + try { + walletStatusObj = JSON.parse($scope.walletStatus); + } catch (e) { + $log.warn('Failed to parse walletStatus.', e); + } + + try { + walletCachedStatusObj = JSON.parse($scope.walletCachedStatus); + } catch (e) { + $log.warn('Failed to parse walletCachedStatus.', e); + } + + if (!displayAsFiat || displayAsFiat && !cryptoBalanceHasBeenDisplayed) { + displayCryptoBalance(walletStatusObj, walletCachedBalance, walletCachedBalanceUpdatedOn, walletCachedStatusObj); + } + + if (displayAsFiat) { + displayFiatBalance(walletStatusObj, walletCachedStatusObj); + } + } + + function getFiatBalance(wallet) { + if (!(wallet.status && wallet.status.isValid)) { + $log.warn('Abandoning call to get fiat balance, because no valid wallet status.'); + return; + } + + txFormatService.formatAlternativeStr(wallet.coin, wallet.status.totalBalanceSat, function onFormatAlernativeStr(formatted) { + if (formatted) { + setDisplay(formatted, ''); + } + }); + } + + function setDisplay(amount, cachedBalanceUpdatedOn) { + $scope.displayAmount = amount; + $scope.cachedBalanceUpdatedOn = cachedBalanceUpdatedOn; + } + } +})(); + diff --git a/src/js/routes.js b/src/js/routes.js index b5a9c91fe..d2b78aac3 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -1277,6 +1277,13 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr }); window.BitAnalytics.ActionHandlers.trackAction(actionTabOpen); + var actionShapeShiftStart = new window.BitAnalytics.ActionFactory.createAction('click', { + name: 'shapeshift_start_click', + class: 'track_shapeshift_start_click', + channels: [channel] + }); + window.BitAnalytics.ActionHandlers.trackAction(actionShapeShiftStart); + // Init language uxLanguage.init(function (lang) { diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js index 86c928a5e..9fad8b0f5 100644 --- a/src/js/services/incomingData.js +++ b/src/js/services/incomingData.js @@ -82,7 +82,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat }); // Timeout is required to enable the "Back" button $timeout(function() { - var params = sendFlowService.getState(); + var params = sendFlowService.getStateClone(); if (amount) { params.amount = amount; @@ -447,7 +447,7 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat amount: thirdPartyData.amount, toAddress: thirdPartyData.toAddress, coin: coin, - thirdParty: JSON.stringify(thirdPartyData) + thirdParty: thirdPartyData }; // fee @@ -455,6 +455,9 @@ angular.module('copayApp.services').factory('incomingData', function($log, $stat stateParams.requiredFeeRate = thirdPartyData.requiredFeeRate * 1024; } + // This does not make sense, thirdPartyData gets added by stateParams below + //sendFlowService.pushState(thirdPartyData); + scannerService.pausePreview(); $state.go('tabs.send', {}, { 'reload': true, diff --git a/src/js/services/sendFlowService.js b/src/js/services/sendFlowService.js index 29686b15b..fcb061c26 100644 --- a/src/js/services/sendFlowService.js +++ b/src/js/services/sendFlowService.js @@ -9,17 +9,21 @@ angular function sendFlowService($log) { var service = { - amount: '', - fromWalletId: '', - sendMax: false, - thirdParty: null, - toAddress: '', - toWalletId: '', + // A separate state variable so we can ensure it is cleared of everything, + // even other properties added that this service does not know about. (such as "coin") + state: { + amount: '', + fromWalletId: '', + sendMax: false, + thirdParty: null, + toAddress: '', + toWalletId: '' + }, previousStates: [], // Functions clear: clear, - getState: getState, + getStateClone: getStateClone, map: map, popState: popState, pushState: pushState, @@ -36,22 +40,24 @@ angular function clearCurrent() { console.log("sendFlow clearCurrent()"); - service.amount = ''; - service.fromWalletId = ''; - service.sendMax = false; - service.thirdParty = null; - service.toAddress = ''; - service.toWalletId = ''; + service.state = { + amount: '', + fromWalletId: '', + sendMax: false, + thirdParty: null, + toAddress: '', + toWalletId: '' + } } /** * Handy for debugging */ - function getState() { + function getStateClone() { var currentState = {}; - Object.keys(service).forEach(function forCurrentParam(key) { - if (typeof service[key] !== 'function' && key !== 'previousStates') { - currentState[key] = service[key]; + Object.keys(service.state).forEach(function forCurrentParam(key) { + if (typeof service.state[key] !== 'function' && key !== 'previousStates') { + currentState[key] = service.state[key]; } }); return currentState; @@ -68,7 +74,7 @@ angular function map(params) { Object.keys(params).forEach(function forNewParam(key) { - service[key] = params[key]; + service.state[key] = params[key]; }); }; @@ -85,7 +91,7 @@ angular function pushState(params) { console.log('sendFlow push'); - var currentParams = getState(); + var currentParams = getStateClone(); service.previousStates.push(currentParams); clearCurrent(); map(params); diff --git a/src/js/services/servicesService.js b/src/js/services/servicesService.js index f2344dd01..316009957 100644 --- a/src/js/services/servicesService.js +++ b/src/js/services/servicesService.js @@ -1,7 +1,12 @@ 'use strict' angular.module('copayApp.services').factory('servicesService', function(configService, $log, lodash) { var root = {}; - var services = []; + var services = [{ + name: 'shapeshift', + title: 'Shapeshift', + icon: 'icon-shapeshift', + sref: 'tabs.shapeshift', + }]; root.register = function(serviceInfo) { $log.info('Adding Services entry:' + serviceInfo.name); diff --git a/src/js/services/shapeshiftService.js b/src/js/services/shapeshiftService.js index 7cf6b4e03..1ce9672ce 100644 --- a/src/js/services/shapeshiftService.js +++ b/src/js/services/shapeshiftService.js @@ -137,17 +137,5 @@ angular.module('copayApp.services').factory('shapeshiftService', function ($http }); }; - var servicesItem = { - name: 'shapeshift', - title: 'Shapeshift', - icon: 'icon-shapeshift', - sref: 'tabs.shapeshift', - }; - - var register = function() { - servicesService.register(servicesItem); - }; - - register(); return root; }); diff --git a/src/sass/buttons.scss b/src/sass/buttons.scss index 41ec3f674..df0ce8945 100644 --- a/src/sass/buttons.scss +++ b/src/sass/buttons.scss @@ -74,7 +74,7 @@ color: #FFF; } &-outline { - @include button-style(transparent, #FFFFFF, #FAFAFA, #FFF, #FFFFFF); + @include button-style(transparent, #FFFFFF, #FFFFFF, #FFFFFF, #FFFFFF); @include button-outline(#FFFFFF); background: none; box-shadow: none; diff --git a/src/sass/components/components.scss b/src/sass/components/components.scss index fb53508b0..4d2bd695e 100644 --- a/src/sass/components/components.scss +++ b/src/sass/components/components.scss @@ -9,3 +9,4 @@ @import "expand-content"; @import "fee-summary"; @import "formatted-amount"; +@import "wallet-balance"; diff --git a/src/sass/components/wallet-balance.scss b/src/sass/components/wallet-balance.scss new file mode 100644 index 000000000..faf4e8611 --- /dev/null +++ b/src/sass/components/wallet-balance.scss @@ -0,0 +1,3 @@ +.wallet-balance-directive { + display: inline-block; +} \ No newline at end of file diff --git a/src/sass/views/amount.scss b/src/sass/views/amount.scss index ca32c6ac4..bf4d3506a 100644 --- a/src/sass/views/amount.scss +++ b/src/sass/views/amount.scss @@ -305,7 +305,8 @@ &.very-long { input, .unit, .primary-amount-display { - font-size: 0.9em; + font-size: 1.2em; // OK for iPhone 5 / SE with BCH to 8dp + @media (min-width: 375px) { font-size: 1.3em; @@ -382,41 +383,46 @@ .available-funds { color: #6F6F70; + text-align: left; + } + + .change-currency { + text-align: right; } .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; + .extra { + flex: 1; line-height: normal; - min-height: auto; - min-width: auto; - padding: 0; - } - .button .icon:before { - font-size: 14px; - line-height: normal; - } - + .button { + background: none; + border: none; + border-radius: 0; + color: #000; + font-family: 'ProximaNova'; + font-size: 14px; + line-height: normal; + min-height: auto; + min-width: auto; + padding: 0; + } - .button { - span { - display: flex; - align-items: center; - justify-content: center; + .button .icon:before { + font-size: 14px; + line-height: normal; + } + + + .button { + span { + display: flex; + align-items: center; + justify-content: center; + } } } } diff --git a/src/sass/views/review.scss b/src/sass/views/review.scss index 79bca1896..c530a1cef 100644 --- a/src/sass/views/review.scss +++ b/src/sass/views/review.scss @@ -1,5 +1,4 @@ #view-review { - background-color: #494949; slide-to-accept, slide-to-accept-success { margin-bottom: constant(safe-area-inset-bottom); /* iOS 11.0 */ diff --git a/src/sass/views/walletDetails.scss b/src/sass/views/walletDetails.scss index 0843a1c07..858229d85 100644 --- a/src/sass/views/walletDetails.scss +++ b/src/sass/views/walletDetails.scss @@ -261,6 +261,7 @@ .no-alternative { padding-top: 45px; } + .item.item-footer { font-weight: lighter; } diff --git a/www/css/main.css b/www/css/main.css index 0dfaaf5fb..8602ba35d 100644 --- a/www/css/main.css +++ b/www/css/main.css @@ -15359,8 +15359,6 @@ log-options #check-bar .checkbox-icon { #view-review .fee-summary { position: absolute; bottom: 92px; } - #view-review .fee-summary-spacer { - height: 15px; } #view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner { box-shadow: none; } #view-review .warning { diff --git a/www/views/amount.html b/www/views/amount.html index 57a8e46ce..b69431b6b 100644 --- a/www/views/amount.html +++ b/www/views/amount.html @@ -14,14 +14,14 @@
+ ng-class="{long: vm.amount.length > 5, 'very-long': vm.amount.length > 8}"> - + {{vm.amount || '0'}} {{vm.unit}}
- + {{vm.alternativeAmount || '0.00'}} {{vm.alternativeUnit}}
@@ -34,17 +34,20 @@
- -
+ +
+
- Available Funds: + Available Funds: +
@@ -90,7 +93,7 @@ +
@@ -41,7 +46,7 @@ class="size-14 amount-alternative" ng-if="status.totalBalanceStr && wallet.network == 'livenet'" ng-style="{opacity: altAmountOpacity}"> - +