Wallet/src/js/controllers/wallet-details.controller.js
2018-09-05 14:11:51 +02:00

489 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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, sendFlowService, storageService, $ionicScrollDelegate, $window, bwcError, gettextCatalog, timeService, feeService, appConfigService, rateService) {
var HISTORY_SHOW_LIMIT = 10;
var currentTxHistoryPage = 0;
var listeners = [];
$scope.txps = [];
$scope.completeTxHistory = [];
$scope.openTxpModal = txpModalService.open;
$scope.isCordova = platformInfo.isCordova;
$scope.isAndroid = platformInfo.isAndroid;
$scope.isIOS = platformInfo.isIOS;
var channel = "ga";
if (platformInfo.isCordova) {
channel = "firebase";
}
var log = new window.BitAnalytics.LogEvent("wallet_details_open", [], [channel]);
window.BitAnalytics.LogEventHandlers.postEvent(log);
$scope.amountIsCollapsible = !$scope.isAndroid;
$scope.openExternalLink = function(url, target) {
externalLinkService.open(url, target);
};
var setPendingTxps = function(txps) {
if (!txps) {
$scope.txps = [];
return;
}
$scope.txps = lodash.sortBy(txps, 'createdOn').reverse();
};
var analyzeUtxosDone;
var analyzeUtxos = function() {
if (analyzeUtxosDone) return;
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
if (err) return;
walletService.getLowUtxos($scope.wallet, levels, function(err, resp) {
if (err || !resp) return;
analyzeUtxosDone = true;
$scope.lowUtxosWarning = resp.warning;
});
});
};
var updateStatus = function(force) {
$scope.updatingStatus = true;
$scope.updateStatusError = null;
$scope.walletNotRegistered = false;
walletService.getStatus($scope.wallet, {
force: !!force,
}, function(err, status) {
$scope.updatingStatus = false;
if (err) {
if (err === 'WALLET_NOT_REGISTERED') {
$scope.walletNotRegistered = true;
} else {
$scope.updateStatusError = bwcError.msg(err, gettextCatalog.getString('Could not update wallet'));
}
$scope.status = null;
} else {
setPendingTxps(status.pendingTxps);
if (!$scope.status || status.balance.totalAmount != $scope.status.balance.totalAmount) {
$scope.status = status;
}
}
$timeout(function() {
$scope.$apply();
});
analyzeUtxos();
});
};
$scope.openSearchModal = function() {
$scope.color = $scope.wallet.color;
$scope.isSearching = true;
$scope.txHistorySearchResults = [];
$scope.filteredTxHistory = [];
$ionicModal.fromTemplateUrl('views/modals/search.html', {
scope: $scope,
focusFirstInput: true
}).then(function(modal) {
$scope.searchModal = modal;
$scope.searchModal.show();
});
$scope.close = function() {
$scope.isSearching = false;
$scope.searchModal.hide();
};
$scope.openTx = function(tx) {
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$scope.close();
$scope.openTxModal(tx);
};
};
$scope.openTxModal = function(btx) {
$scope.btx = lodash.cloneDeep(btx);
$scope.walletId = $scope.wallet.id;
$state.transitionTo('tabs.wallet.tx-details', {
txid: $scope.btx.txid,
walletId: $scope.walletId
});
};
$scope.openBalanceModal = function() {
$ionicModal.fromTemplateUrl('views/modals/wallet-balance.html', {
scope: $scope
}).then(function(modal) {
$scope.walletBalanceModal = modal;
$scope.walletBalanceModal.show();
});
$scope.close = function() {
$scope.walletBalanceModal.hide();
};
};
$scope.recreate = function() {
walletService.recreate($scope.wallet, function(err) {
if (err) return;
$timeout(function() {
walletService.startScan($scope.wallet, function() {
$scope.updateAll();
$scope.$apply();
});
});
});
};
var updateTxHistory = function(cb) {
if (!cb) cb = function() {};
$scope.updateTxHistoryError = false;
$scope.updatingTxHistoryProgress = 0;
feeService.getFeeLevels($scope.wallet.coin, function(err, levels) {
walletService.getTxHistory($scope.wallet, {
feeLevels: levels
}, function(err, txHistory) {
$scope.updatingTxHistory = false;
if (err) {
$scope.txHistory = null;
$scope.updateTxHistoryError = true;
return;
}
applyCurrencyAliases(txHistory);
var config = configService.getSync();
var fiatCode = config.wallet.settings.alternativeIsoCode;
lodash.each(txHistory, function(t) {
var r = rateService.toFiat(t.amount, fiatCode, $scope.wallet.coin);
t.alternativeAmountStr = r.toFixed(2) + ' ' + fiatCode;
});
$scope.completeTxHistory = txHistory;
$scope.showHistory();
$timeout(function() {
$scope.$apply();
});
return cb();
});
});
};
function applyCurrencyAliases(txHistory) {
var defaults = configService.getDefaults();
var configCache = configService.getSync();
lodash.each(txHistory, function(t) {
t.amountUnitStr = $scope.wallet.coin == 'btc'
? (configCache.bitcoinAlias || defaults.bitcoinAlias)
: (configCache.bitcoinCashAlias || defaults.bitcoinCashAlias);
t.amountUnitStr = t.amountUnitStr.toUpperCase();
});
}
$scope.showHistory = function() {
if ($scope.completeTxHistory) {
$scope.txHistory = $scope.completeTxHistory.slice(0, (currentTxHistoryPage + 1) * HISTORY_SHOW_LIMIT);
$scope.txHistoryShowMore = $scope.completeTxHistory.length > $scope.txHistory.length;
}
};
$scope.getDate = function(txCreated) {
var date = new Date(txCreated * 1000);
return date;
};
$scope.isFirstInGroup = function(index) {
if (index === 0) {
return true;
}
var curTx = $scope.txHistory[index];
var prevTx = $scope.txHistory[index - 1];
return !$scope.createdDuringSameMonth(curTx, prevTx);
};
$scope.isLastInGroup = function(index) {
if (index === $scope.txHistory.length - 1) {
return true;
}
return $scope.isFirstInGroup(index + 1);
};
$scope.createdDuringSameMonth = function(curTx, prevTx) {
return timeService.withinSameMonth(curTx.time * 1000, prevTx.time * 1000);
};
$scope.createdWithinPastDay = function(time) {
return timeService.withinPastDay(time);
};
$scope.isDateInCurrentMonth = function(date) {
return timeService.isDateInCurrentMonth(date);
};
$scope.isUnconfirmed = function(tx) {
return !tx.confirmations || tx.confirmations === 0;
};
$scope.showMore = function() {
$timeout(function() {
currentTxHistoryPage++;
$scope.showHistory();
$scope.$broadcast('scroll.infiniteScrollComplete');
}, 100);
};
$scope.onRefresh = function() {
$timeout(function() {
$scope.$broadcast('scroll.refreshComplete');
}, 300);
$scope.updateAll(true);
};
$scope.updateAll = function(force, cb)  {
updateStatus(force);
updateTxHistory(cb);
};
$scope.hideToggle = function() {
profileService.toggleHideBalanceFlag($scope.wallet.credentials.walletId, function(err) {
if (err) $log.error(err);
});
};
var prevPos;
$scope.txHistoryPaddingBottom = 0;
function getScrollPosition() {
var scrollPosition = $ionicScrollDelegate.getScrollPosition();
if (!scrollPosition) {
$timeout(function() {
getScrollPosition();
}, 25);
return;
}
var pos = scrollPosition.top;
if (pos > 0) {
$scope.txHistoryPaddingBottom = "200px";
}
if (pos === prevPos) {
$timeout(function() {
getScrollPosition();
}, 25);
return;
}
prevPos = pos;
refreshAmountSection(pos);
}
function refreshAmountSection(scrollPos) {
var AMOUNT_HEIGHT_BASE = 210;
$scope.showBalanceButton = false;
if ($scope.status) {
$scope.showBalanceButton = ($scope.status.totalBalanceSat != $scope.status.spendableAmount);
if ($scope.showBalanceButton) {
AMOUNT_HEIGHT_BASE = 270;
}
}
if (!$scope.amountIsCollapsible) {
var t = ($scope.showBalanceButton ? 15 : 45);
$scope.amountScale = 'translateY(' + t + 'px)';
return;
}
scrollPos = scrollPos || 0;
var amountHeight = AMOUNT_HEIGHT_BASE - scrollPos;
if (amountHeight < 80) {
amountHeight = 80;
}
var contentMargin = amountHeight;
if (contentMargin > AMOUNT_HEIGHT_BASE) {
contentMargin = AMOUNT_HEIGHT_BASE;
}
var amountScale = (amountHeight / AMOUNT_HEIGHT_BASE);
if (amountScale < 0.5) {
amountScale = 0.5;
}
if (amountScale > 1.1) {
amountScale = 1.1;
}
var s = amountScale;
// Make space for the balance button when it needs to display.
var TOP_NO_BALANCE_BUTTON = 115;
var TOP_BALANCE_BUTTON = 30;
var top = TOP_NO_BALANCE_BUTTON;
if ($scope.showBalanceButton) {
top = TOP_BALANCE_BUTTON;
}
var amountTop = ((amountScale - 0.80) / 0.80) * top;
if (amountTop < -2) {
amountTop = -2;
}
if (amountTop > top) {
amountTop = top;
}
var t = amountTop;
if (scrollPos > 50) {
contentMargin = amountHeight = 80;
$scope.altAmountOpacity = 0.01;
$scope.buttonsOpacity = 0.01;
} else {
contentMargin = amountHeight = 210;
$scope.altAmountOpacity = 1;
$scope.buttonsOpacity = 1;
}
$scope.amountHeight = amountHeight + 'px';
$scope.contentMargin = contentMargin + 'px';
$scope.amountScale = 'scale3d(' + s + ',' + s + ',' + s + ') translateY(' + t + 'px)';
$scope.$digest();
getScrollPosition();
}
var scrollEffectTimeout;
var scrollWatcherInitialized;
$scope.$on("$ionicView.enter", function(event, data) {
if ($scope.isCordova && $scope.isAndroid) setAndroidStatusBarColor();
if (scrollWatcherInitialized || !$scope.amountIsCollapsible) {
return;
}
scrollWatcherInitialized = true;
});
$scope.$on("$ionicView.beforeEnter", function(event, data) {
configService.whenAvailable(function (config) {
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
$timeout(function () {
$scope.$apply();
});
});
$scope.walletId = data.stateParams.walletId;
$scope.wallet = profileService.getWallet($scope.walletId);
if (!$scope.wallet) return;
$scope.requiresMultipleSignatures = $scope.wallet.credentials.m > 1;
$scope.updatingTxHistory = true;
addressbookService.list(function(err, ab) {
if (err) $log.error(err);
$scope.addressbook = ab || {};
});
listeners = [
$rootScope.$on('bwsEvent', function(e, walletId) {
if (walletId == $scope.wallet.id && e.type != 'NewAddress')
$scope.updateAll();
}),
$rootScope.$on('Local/TxAction', function(e, walletId) {
if (walletId == $scope.wallet.id)
$scope.updateAll();
}),
];
});
var refreshInterval;
$scope.$on("$ionicView.afterEnter", function(event, data) {
$scope.updateAll();
refreshAmountSection();
refreshInterval = $interval($scope.onRefresh, 10 * 1000);
});
$scope.$on("$ionicView.afterLeave", function(event, data) {
$interval.cancel(refreshInterval);
if ($window.StatusBar) {
$window.StatusBar.backgroundColorByHexString('#000000');
}
});
$scope.$on("$ionicView.leave", function(event, data) {
lodash.each(listeners, function(x) {
x();
});
});
function setAndroidStatusBarColor() {
var SUBTRACT_AMOUNT = 15;
var walletColor;
if (!$scope.wallet.color) walletColor = appConfigService.name == 'copay' ? '#019477' : '#4a90e2';
else walletColor = $scope.wallet.color;
var rgb = hexToRgb(walletColor);
var keys = Object.keys(rgb);
keys.forEach(function(k) {
if (rgb[k] - SUBTRACT_AMOUNT < 0) {
rgb[k] = 0;
} else {
rgb[k] -= SUBTRACT_AMOUNT;
}
});
var statusBarColorHexString = rgbToHex(rgb.r, rgb.g, rgb.b);
if ($window.StatusBar)
$window.StatusBar.backgroundColorByHexString(statusBarColorHexString);
}
function hexToRgb(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, function(m, r, g, b) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
$scope.goToSend = function() {
sendFlowService.start({
fromWalletId: $scope.wallet.id
});
};
$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');
});
};
});