Compare commits
27 commits
wallet/spr
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e73e4c16d | ||
|
|
06f1f4df91 | ||
|
|
70f520ea43 | ||
|
|
f1d2b00b2b | ||
|
|
4e524a7500 | ||
|
|
c0a14fe11c | ||
|
|
d47393f160 | ||
|
|
898b7d03fe | ||
|
|
49a6711970 |
||
|
|
d72a71d920 | ||
|
|
4d623d51d7 | ||
|
|
e02368ea3c | ||
|
|
92c1b4b346 | ||
|
|
f3902ba3f0 | ||
|
|
9438960e24 | ||
|
|
d55fc70e7d | ||
|
|
c1f6e56b56 | ||
|
|
7d29490b0f | ||
|
|
51c35101a6 | ||
|
|
ce02025991 | ||
|
|
d2715016b5 | ||
|
|
3afbdf73d3 |
||
|
|
00e4f8ac39 | ||
|
|
8c62bc445a | ||
|
|
1c450418b8 |
||
|
|
15b161d55d |
||
|
|
e127b81e7c |
23 changed files with 373 additions and 334 deletions
14
.vscode/launch.json
vendored
Normal file
14
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"program": "${workspaceFolder}/www/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ module.exports = function(grunt) {
|
|||
sign_android: {
|
||||
// When the build log outputs "Built the following apk(s):", it seems to need the filename to start with "android-release".
|
||||
// It looks like it simply lists all apk files starting with "android-release"
|
||||
command: 'rm -f platforms/android/build/outputs/apk/release/*-android-signed-aligned.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/release/android-release-signed.apk platforms/android/build/outputs/apk/release/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk',
|
||||
command: 'rm -f platforms/android/build/outputs/apk/android-release-signed-*.apk; jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ../bitcoin-com-release-key.jks -signedjar platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/android-release-unsigned.apk bitcoin-com && zipalign -v 4 platforms/android/build/outputs/apk/android-release-signed.apk platforms/android/build/outputs/apk/bitcoin-com-wallet-<%= pkg.fullVersion %>-android-signed-aligned.apk',
|
||||
stdin: true,
|
||||
},
|
||||
sign_desktop_dist: {
|
||||
|
|
|
|||
21
README.md
21
README.md
|
|
@ -1,3 +1,24 @@
|
|||
This is a fork of the Bitcoin.com wallet to add additional features.
|
||||
Features included:
|
||||
|
||||
- Zero fee transactions (only works for Bitcoin Cash). You will be asked for, if you want to send a transaction as zero fee on the confirmation page.
|
||||
|
||||
## Zero fee transactions:
|
||||
Because most network nodes on the Bitcoin Cash network don't relay zero fee txs, you will experience some strange issues, but don't worry: for me personally the Bitcoin.com pool has included all my zero fee transactions, but please beware that the receiver probably won't see your tx before it has been confirmed and please do also keep in mind, that the transactions coming after it won't confirm or be seen before the zero fee one has been confirmed.
|
||||
|
||||
If you do already have a Bitcoin.com wallet, you need to create a new one to use this feature or change the wallet URL to: https://bws.freepages.dk/bws/api
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Please beware this is my personal experimental project. You are more than welcome to play with it, but I don't take any responsibility of loss of funds due to errors in the code, so please make sure you made a backup before running this software.
|
||||
|
||||
## Builds
|
||||
|
||||
You can build the software yourself using the instructions below or use prebuilt binaries which can be found here (currently Windows and Linux only): https://ipfs.io/ipfs/QmR1DaS3QsDS48SzAWKUWFfmtMfJc4tgMtkSk3JFmuzewe
|
||||
|
||||
##
|
||||
|
||||
|
||||
The Bitcoin.com wallet is a fork of the Copay Wallet (https://github.com/bitpay/copay).
|
||||
|
||||
The Bitcoin.com wallet is a secure bitcoin wallet platform for both desktop and mobile devices. It uses [Bitcore Wallet Service](https://github.com/Bitcoin-com/bitcore-wallet-service) (our fork of the [Bitpay Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)) (BWS) for peer synchronization and network interfacing.
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@
|
|||
"windowsAppId": "804636ee-b017-4cad-8719-e58ac97ffa5c",
|
||||
"pushSenderId": "1036948132229",
|
||||
"description": "A Secure Bitcoin Wallet",
|
||||
"version": "5.0.2",
|
||||
"fullVersion": "5.0-rc3",
|
||||
"androidVersion": "500200",
|
||||
"version": "5.1.3",
|
||||
"fullVersion": "5.1-rc2",
|
||||
"androidVersion": "501003",
|
||||
"_extraCSS": "",
|
||||
"_enabledExtensions": {
|
||||
"coinbase": false,
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
</plugin>
|
||||
<!-- Supported Platforms -->
|
||||
<engine name="ios" spec="~4.5.3" />
|
||||
<engine name="android" spec="~6.4.0" />
|
||||
<engine name="android" spec="~6.3.0" />
|
||||
<engine name="windows" spec="~5.0.0" />
|
||||
|
||||
<!-- Platform Specific Settings -->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
ext {
|
||||
ANDROID_SUPPORT_V4_VERSION = '26.1.0'
|
||||
ANDROID_SUPPORT_ANNOTATIONS_VERSION = '26.1.0'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,31 @@ angular
|
|||
|
||||
function amountController(configService, $filter, gettextCatalog, $ionicHistory, $ionicModal, $ionicScrollDelegate, lodash, $log, nodeWebkitService, rateService, $scope, $state, $timeout, sendFlowService, shapeshiftService, txFormatService, platformInfo, ongoingProcess, popupService, profileService, walletService, $window) {
|
||||
var vm = this;
|
||||
|
||||
|
||||
// Variables
|
||||
vm.allowSend = false;
|
||||
vm.altCurrencyList = [];
|
||||
vm.alternativeAmount = '';
|
||||
vm.alternativeUnit = '';
|
||||
vm.amount = '0';
|
||||
vm.availableFunds = '';
|
||||
vm.canSendAllAvailableFunds = true;
|
||||
vm.errorMessage = '';
|
||||
// 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.sendableFunds = '';
|
||||
vm.showSendMaxButton = false;
|
||||
vm.showSendLimitMaxButton = false;
|
||||
vm.thirdParty = false;
|
||||
vm.unit = '';
|
||||
|
||||
// Functions
|
||||
vm.changeUnit = changeUnit;
|
||||
vm.close = close;
|
||||
|
|
@ -22,6 +46,7 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
|
|||
vm.removeDigit = removeDigit;
|
||||
vm.save = save;
|
||||
vm.sendMax = sendMax;
|
||||
|
||||
|
||||
$scope.$on('$ionicView.beforeEnter', onBeforeEnter);
|
||||
$scope.$on('$ionicView.leave', onLeave);
|
||||
|
|
@ -54,55 +79,8 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
|
|||
satoshis: null
|
||||
};
|
||||
|
||||
function initVariables() {
|
||||
// Private variables
|
||||
altCurrencyModal = null;
|
||||
altUnitIndex = 0;
|
||||
availableUnits = [];
|
||||
canSendMax = true;
|
||||
fiatCode;
|
||||
isNW = platformInfo.isNW;
|
||||
isAndroid = platformInfo.isAndroid;
|
||||
isIos = platformInfo.isIOS;
|
||||
lastUsedAltCurrencyList = [];
|
||||
passthroughParams = {};
|
||||
satToUnit;
|
||||
transactionSendableAmount = {
|
||||
crypto: '',
|
||||
satoshis: null
|
||||
};
|
||||
unitDecimals;
|
||||
unitIndex = 0;
|
||||
unitToSatoshi;
|
||||
useSendMax = false;
|
||||
walletSpendableAmount = {
|
||||
crypto: '',
|
||||
satoshis: null
|
||||
};
|
||||
|
||||
// Public variables
|
||||
vm.allowSend = false;
|
||||
vm.altCurrencyList = [];
|
||||
vm.alternativeAmount = '';
|
||||
vm.alternativeUnit = '';
|
||||
vm.amount = '0';
|
||||
vm.availableFunds = '';
|
||||
vm.canSendAllAvailableFunds = true;
|
||||
vm.errorMessage = '';
|
||||
// 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.sendableFunds = '';
|
||||
vm.showSendMaxButton = false;
|
||||
vm.showSendLimitMaxButton = false;
|
||||
vm.thirdParty = null;
|
||||
vm.unit = '';
|
||||
function onLeave() {
|
||||
angular.element($window).off('keydown');
|
||||
}
|
||||
|
||||
function onBeforeEnter(event, data) {
|
||||
|
|
@ -110,19 +88,11 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
|
|||
sendFlowService.state.pop();
|
||||
}
|
||||
|
||||
|
||||
// Init before entering on this screen
|
||||
initVariables();
|
||||
initCurrencies();
|
||||
// Then start
|
||||
|
||||
passthroughParams = sendFlowService.state.getClone();
|
||||
console.log('amount onBeforeEnter after back sendflow ', passthroughParams);
|
||||
|
||||
|
||||
// Init thirdParty, should be done for all the variable
|
||||
vm.thirdParty = null;
|
||||
|
||||
vm.fromWalletId = passthroughParams.fromWalletId;
|
||||
vm.toWalletId = passthroughParams.toWalletId;
|
||||
vm.minAmount = parseFloat(passthroughParams.minAmount);
|
||||
|
|
@ -248,10 +218,6 @@ function amountController(configService, $filter, gettextCatalog, $ionicHistory,
|
|||
}
|
||||
}
|
||||
|
||||
function onLeave() {
|
||||
angular.element($window).off('keydown');
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
sendFlowService.router.goBack();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -517,7 +517,8 @@ angular.module('copayApp.controllers').controller('confirmController', function(
|
|||
if (!lodash.isEmpty(warningMsg))
|
||||
msg += '\n' + warningMsg;
|
||||
|
||||
popupService.showAlert(null, msg, function() {});
|
||||
popupService.showAlert(null, msg, function() {});
|
||||
|
||||
};
|
||||
|
||||
$scope.onWalletSelect = function(wallet) {
|
||||
|
|
|
|||
|
|
@ -6,9 +6,58 @@ angular
|
|||
.module('copayApp.controllers')
|
||||
.controller('reviewController', reviewController);
|
||||
|
||||
function reviewController(addressbookService, externalLinkService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, lodash, $log, ongoingProcess, platformInfo, popupService, profileService, $scope, sendFlowService, shapeshiftService, soundService, $state, $timeout, txConfirmNotification, txFormatService, walletService) {
|
||||
function reviewController(addressbookService, bitcoinCashJsService, bitcore, bitcoreCash, bwcError, clipboardService, configService, feeService, gettextCatalog, $interval, $ionicHistory, $ionicModal, ionicToast, 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.displayAddress = '';
|
||||
vm.feeCrypto = '';
|
||||
vm.feeFiat = '';
|
||||
vm.fiatCurrency = '';
|
||||
vm.feeIsHigh = false;
|
||||
vm.feeLessThanACent = false;
|
||||
vm.isCordova = platformInfo.isCordova;
|
||||
vm.memo = '';
|
||||
vm.notReadyMessage = '';
|
||||
vm.origin = {
|
||||
balanceAmount: '',
|
||||
balanceCurrency: '',
|
||||
currency: '',
|
||||
currencyColor: '',
|
||||
};
|
||||
vm.originWallet = null;
|
||||
vm.paymentExpired = false;
|
||||
vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here');
|
||||
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;
|
||||
vm.onShareTransaction = onShareTransaction;
|
||||
|
||||
var sendFlowData;
|
||||
var config = null;
|
||||
var coin = '';
|
||||
|
|
@ -27,90 +76,23 @@ angular
|
|||
var unitFromSat = 0;
|
||||
|
||||
var FEE_TOO_HIGH_LIMIT_PERCENTAGE = 15;
|
||||
|
||||
// Functions
|
||||
vm.goBack = goBack;
|
||||
vm.onSuccessConfirm = onSuccessConfirm;
|
||||
vm.onShareTransaction = onShareTransaction;
|
||||
|
||||
function initVariables() {
|
||||
// Private variables
|
||||
sendFlowData;
|
||||
config = null;
|
||||
coin = '';
|
||||
countDown = null;
|
||||
defaults = {};
|
||||
usingCustomFee = false;
|
||||
usingMerchantFee = false;
|
||||
destinationWalletId = '';
|
||||
lastTxId = '';
|
||||
originWalletId = '';
|
||||
priceDisplayIsFiat = true;
|
||||
satoshis = null;
|
||||
toAddress = '';
|
||||
tx = {};
|
||||
txPayproData = null;
|
||||
unitFromSat = 0;
|
||||
|
||||
// Public variables
|
||||
vm.buttonText = '';
|
||||
vm.destination = {
|
||||
address: '',
|
||||
balanceAmount: '',
|
||||
balanceCurrency: '',
|
||||
coin: '',
|
||||
color: '',
|
||||
currency: '',
|
||||
currencyColor: '',
|
||||
kind: '', // 'address', 'contact', 'wallet'
|
||||
name: ''
|
||||
};
|
||||
vm.displayAddress = '';
|
||||
vm.feeCrypto = '';
|
||||
vm.feeFiat = '';
|
||||
vm.fiatCurrency = '';
|
||||
vm.feeIsHigh = false;
|
||||
vm.feeLessThanACent = false;
|
||||
vm.isCordova = platformInfo.isCordova;
|
||||
vm.memo = '';
|
||||
vm.notReadyMessage = '';
|
||||
vm.origin = {
|
||||
balanceAmount: '',
|
||||
balanceCurrency: '',
|
||||
currency: '',
|
||||
currencyColor: '',
|
||||
};
|
||||
vm.originWallet = null;
|
||||
vm.paymentExpired = false;
|
||||
vm.personalNotePlaceholder = gettextCatalog.getString('Enter text here');
|
||||
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 = null;
|
||||
vm.wallet = null;
|
||||
vm.memoExpanded = false;
|
||||
}
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
|
||||
|
||||
function onBeforeEnter(event, data) {
|
||||
$log.debug('reviewController onBeforeEnter sendflow ', sendFlowService.state);
|
||||
|
||||
// Init before entering on this screen
|
||||
initVariables();
|
||||
// Then start
|
||||
function onBeforeEnter(event, data) {
|
||||
console.log('review onBeforeEnter sendflow ', sendFlowService.state);
|
||||
// Reset from last time
|
||||
vm.memo = '';
|
||||
|
||||
defaults = configService.getDefaults();
|
||||
sendFlowData = sendFlowService.state.getClone();
|
||||
originWalletId = sendFlowData.fromWalletId;
|
||||
satoshis = parseInt(sendFlowData.amount, 10);
|
||||
if (typeof sendFlowData.amount === 'string') {
|
||||
satoshis = parseInt(sendFlowData.amount, 10);
|
||||
} else {
|
||||
satoshis = sendFlowData.amount;
|
||||
}
|
||||
toAddress = sendFlowData.toAddress;
|
||||
destinationWalletId = sendFlowData.toWalletId;
|
||||
|
||||
|
|
@ -123,14 +105,11 @@ angular
|
|||
vm.thirdParty = sendFlowData.thirdParty;
|
||||
switch (vm.thirdParty.id) {
|
||||
case 'shapeshift':
|
||||
initShapeshift(function onInitShapeshift(err) {
|
||||
initShapeshift(function (err) {
|
||||
if (err) {
|
||||
// Error stop here
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
popupService.showConfirm(gettextCatalog.getString('Shapeshift Error'), err.toString(), gettextCatalog.getString('Open') + " Shapeshift", gettextCatalog.getString('Go Back'), function onConfirm(hasConfirm) {
|
||||
if (hasConfirm) {
|
||||
externalLinkService.open("https://shapeshift.io");
|
||||
}
|
||||
popupService.showAlert(gettextCatalog.getString('Shapeshift Error'), err.toString(), function () {
|
||||
$ionicHistory.goBack();
|
||||
});
|
||||
} else {
|
||||
|
|
@ -156,6 +135,7 @@ angular
|
|||
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();
|
||||
|
|
@ -172,18 +152,18 @@ angular
|
|||
if (!tx || !vm.originWallet) return;
|
||||
|
||||
if (vm.paymentExpired) {
|
||||
popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function onAlert() {
|
||||
popupService.showAlert(null, gettextCatalog.getString('This bitcoin payment request has expired.', function () {
|
||||
$ionicHistory.goBack();
|
||||
}));
|
||||
vm.sendStatus = '';
|
||||
$timeout(function onTimeout() {
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
ongoingProcess.set('creatingTx', true, statusChangeHandler);
|
||||
getTxp(lodash.clone(tx), vm.originWallet, false, function onGetTxp(err, txp) {
|
||||
getTxp(lodash.clone(tx), vm.originWallet, false, function(err, txp) {
|
||||
ongoingProcess.set('creatingTx', false, statusChangeHandler);
|
||||
if (err) return;
|
||||
|
||||
|
|
@ -200,12 +180,12 @@ angular
|
|||
if (!vm.originWallet.canSign() && !vm.originWallet.isPrivKeyExternal()) {
|
||||
$log.info('No signing proposal: No private key');
|
||||
|
||||
return walletService.onlyPublish(vm.originWallet, txp, function onOnlyPublish(err) {
|
||||
return walletService.onlyPublish(vm.originWallet, txp, function(err) {
|
||||
if (err) setSendError(err);
|
||||
}, statusChangeHandler);
|
||||
}
|
||||
|
||||
walletService.publishAndSign(vm.originWallet, txp, function onPublishAndSign(err, txp) {
|
||||
walletService.publishAndSign(vm.originWallet, txp, function(err, txp) {
|
||||
if (err) return setSendError(err);
|
||||
if (config.confirmedTxsNotifications && config.confirmedTxsNotifications.enabled) {
|
||||
txConfirmNotification.subscribe(vm.originWallet, {
|
||||
|
|
@ -219,7 +199,7 @@ angular
|
|||
confirmTx(function(nok) {
|
||||
if (nok) {
|
||||
vm.sendStatus = '';
|
||||
$timeout(function onTimeout() {
|
||||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
return;
|
||||
|
|
@ -276,6 +256,7 @@ angular
|
|||
};
|
||||
|
||||
function createVanityTransaction(data) {
|
||||
console.log('createVanityTransaction()');
|
||||
var configFeeLevel = config.wallet.settings.feeLevel ? config.wallet.settings.feeLevel : 'normal';
|
||||
|
||||
// Grab stateParams
|
||||
|
|
@ -299,9 +280,6 @@ angular
|
|||
txp: {},
|
||||
};
|
||||
|
||||
if (vm.thirdParty && vm.thirdParty.id === "shapeshift") {
|
||||
tx.toAddress = vm.thirdParty.toAddress;
|
||||
}
|
||||
|
||||
if (data.stateParams.requiredFeeRate) {
|
||||
vm.usingMerchantFee = true;
|
||||
|
|
@ -315,35 +293,29 @@ angular
|
|||
var B = tx.coin === 'bch' ? bitcoreCash : bitcore;
|
||||
var networkName;
|
||||
try {
|
||||
// Final destination is a wallet, but this transaction must go to an address for the first stage of the exchange.
|
||||
if (sendFlowData.thirdParty && sendFlowData.thirdParty.id === 'shapeshift') {
|
||||
networkName = (new B.Address(tx.toAddress)).network.name;
|
||||
tx.network = networkName;
|
||||
setupTx(tx);
|
||||
|
||||
} else if (vm.destination.kind === 'wallet') { // This is a wallet-to-wallet transfer
|
||||
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) {
|
||||
if (err) {
|
||||
$log.error('Error getting address for wallet.', err);
|
||||
throw new Error(err.message);
|
||||
}
|
||||
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) {
|
||||
$log.error('Error setting up tx', e);
|
||||
console.error('Error setting up tx', e);
|
||||
var message = gettextCatalog.getString('Invalid address');
|
||||
popupService.showAlert(null, message, function () {
|
||||
$ionicHistory.nextViewOptions({
|
||||
|
|
@ -413,7 +385,7 @@ angular
|
|||
}
|
||||
txp.excludeUnconfirmedUtxos = !tx.spendUnconfirmed;
|
||||
txp.dryRun = dryRun;
|
||||
walletService.createTx(wallet, txp, function onCreateTx(err, ctxp) {
|
||||
walletService.createTx(wallet, txp, function(err, ctxp) {
|
||||
if (err) {
|
||||
setSendError(err);
|
||||
return cb(err);
|
||||
|
|
@ -433,7 +405,7 @@ angular
|
|||
var walletStatus = null;
|
||||
if (wallet.status && wallet.status.isValid) {
|
||||
walletStatus = wallet.status;
|
||||
} else if (wallet.cachedStatus && wallet.cachedStatus.isValid) {
|
||||
} else if (wallet.cachedStatus.isValid) {
|
||||
walletStatus = wallet.cachedStatus;
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +444,7 @@ angular
|
|||
}
|
||||
|
||||
// Check if the recipient is a contact
|
||||
addressbookService.get(originCoin + address, function onGetContact(err, contact) {
|
||||
addressbookService.get(originCoin + address, function(err, contact) {
|
||||
if (!err && contact) {
|
||||
handleDestinationAsAddressOfContact(contact);
|
||||
} else {
|
||||
|
|
@ -545,6 +517,7 @@ angular
|
|||
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) {
|
||||
|
|
@ -562,8 +535,9 @@ angular
|
|||
if (err) {
|
||||
return cb(err);
|
||||
} else {
|
||||
// Want it to appear like a wallet-to-wallet transfer, so don't set the main toAddress.
|
||||
vm.thirdParty.toAddress = shapeshiftData.toAddress;
|
||||
vm.destination.kind = 'shapeshift';
|
||||
vm.destination.address = toAddress;
|
||||
tx.toAddress = shapeshiftData.toAddress;
|
||||
vm.memo = 'ShapeShift Order:\nhttps://www.shapeshift.io/#/status/' + shapeshiftData.orderId;
|
||||
vm.memoExpanded = !!vm.memo;
|
||||
ongoingProcess.set('connectingShapeshift', false);
|
||||
|
|
@ -598,6 +572,7 @@ angular
|
|||
}, 1000);
|
||||
|
||||
function setExpirationTime() {
|
||||
console.log('setExpirationTime()');
|
||||
var now = Math.floor(Date.now() / 1000);
|
||||
|
||||
if (now > expirationTime) {
|
||||
|
|
@ -715,7 +690,7 @@ angular
|
|||
$timeout(function() {
|
||||
$scope.$apply();
|
||||
});
|
||||
popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function onAlert() {
|
||||
popupService.showAlert(gettextCatalog.getString('Error at confirm'), bwcError.msg(msg), function () {
|
||||
$ionicHistory.goBack();
|
||||
});
|
||||
};
|
||||
|
|
@ -727,7 +702,7 @@ angular
|
|||
tx.displayAddress = tx.toAddress;
|
||||
}
|
||||
|
||||
addressbookService.get(tx.coin+tx.toAddress, function onGetContact(err, addr) { // Check if the recipient is a contact
|
||||
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;
|
||||
|
|
@ -743,13 +718,35 @@ angular
|
|||
if (tx.paypro)
|
||||
startExpirationTimer(tx.paypro.expires);
|
||||
|
||||
updateTx(tx, vm.originWallet, {
|
||||
dryRun: true
|
||||
}, function(err) {
|
||||
$timeout(function onTimeout() {
|
||||
$scope.$apply();
|
||||
}, 10);
|
||||
});
|
||||
popupService.showConfirm(null, 'Do you want this transaction to be sent without a fee?', 'Yes', 'No', function(ok) {
|
||||
if(ok){
|
||||
tx.feeRate = 0;
|
||||
// tx.feeLevel = 'free';
|
||||
usingCustomFee = true;
|
||||
}
|
||||
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) {
|
||||
|
|
@ -790,6 +787,7 @@ angular
|
|||
msg += '\n' + warningMsg;
|
||||
|
||||
popupService.showAlert(null, msg, function() {});
|
||||
//popupService.showConfirm(null, msg, null, null, function() {});
|
||||
};
|
||||
|
||||
function statusChangeHandler(processName, showName, isOn) {
|
||||
|
|
@ -860,14 +858,18 @@ angular
|
|||
// refresh();
|
||||
|
||||
var feeServiceLevel = usingMerchantFee && vm.originWallet.coin == 'btc' ? 'urgent' : tx.feeLevel;
|
||||
feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function onGetFeeRate(err, feeRate) {
|
||||
feeService.getFeeRate(vm.originWallet.coin, tx.network, feeServiceLevel, function(err, feeRate) {
|
||||
if (err) {
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
var msg;
|
||||
if (usingCustomFee) {
|
||||
// if (tx.feeLevel == 'free'){
|
||||
// tx.feeRate = 0;
|
||||
// }
|
||||
// else
|
||||
if (usingCustomFee) {
|
||||
msg = gettextCatalog.getString('Custom');
|
||||
tx.feeLevelName = msg;
|
||||
} else if (usingMerchantFee) {
|
||||
|
|
@ -877,9 +879,11 @@ angular
|
|||
} else {
|
||||
tx.feeLevelName = feeService.feeOpts[tx.feeLevel];
|
||||
tx.feeRate = feeRate;
|
||||
|
||||
}
|
||||
|
||||
|
||||
getSendMaxInfo(lodash.clone(tx), wallet, function onGetSendmaxInfo(err, sendMaxInfo) {
|
||||
getSendMaxInfo(lodash.clone(tx), wallet, function(err, sendMaxInfo) {
|
||||
if (err) {
|
||||
ongoingProcess.set('calculatingFee', false);
|
||||
var msg = gettextCatalog.getString('Error getting SendMax information');
|
||||
|
|
@ -893,7 +897,7 @@ angular
|
|||
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'), function onAlert() {
|
||||
popupService.showAlert(gettextCatalog.getString('Error'), gettextCatalog.getString('Not enough funds for fee'), function () {
|
||||
$ionicHistory.goBack();
|
||||
});
|
||||
return cb('no_funds');
|
||||
|
|
@ -918,7 +922,8 @@ angular
|
|||
return cb();
|
||||
}
|
||||
|
||||
getTxp(lodash.clone(tx), wallet, opts.dryRun, function onGetTxp(err, txp) {
|
||||
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') {
|
||||
|
|
@ -930,7 +935,7 @@ angular
|
|||
}
|
||||
|
||||
txp.feeStr = txFormatService.formatAmountStr(wallet.coin, txp.fee);
|
||||
txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function onFormatAlternativeStr(v) {
|
||||
txFormatService.formatAlternativeStr(wallet.coin, txp.fee, function(v) {
|
||||
// txp.alternativeFeeStr = v;
|
||||
// if (txp.alternativeFeeStr.substring(0, 4) == '0.00')
|
||||
// txp.alternativeFeeStr = '< ' + txp.alternativeFeeStr;
|
||||
|
|
@ -939,6 +944,8 @@ angular
|
|||
if (v.substring(0, 1) === "<") {
|
||||
vm.feeLessThanACent = true;
|
||||
}
|
||||
|
||||
console.log("fiat", vm.feeFiat);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -948,11 +955,14 @@ angular
|
|||
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,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('servicesController', function($scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
|
||||
angular.module('copayApp.controllers').controller('servicesController', function(externalLinkService, $scope, $ionicScrollDelegate, $timeout, servicesService, configService) {
|
||||
$scope.hide = false;
|
||||
|
||||
configService.whenAvailable(function(config) {
|
||||
|
|
@ -20,4 +20,8 @@ angular.module('copayApp.controllers').controller('servicesController', function
|
|||
}, 10);
|
||||
};
|
||||
|
||||
$scope.open = function(url) {
|
||||
externalLinkService.open(url, false);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,17 +12,6 @@ angular
|
|||
var unitDecimals = 0;
|
||||
var unitsFromSatoshis = 0;
|
||||
|
||||
//
|
||||
// Needs to migrate $scope to vm.
|
||||
//
|
||||
function initVariables() {
|
||||
// Private variables
|
||||
fromWalletId = '';
|
||||
priceDisplayAsFiat = false;
|
||||
unitDecimals = 0;
|
||||
unitsFromSatoshis = 0;
|
||||
}
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", onBeforeEnter);
|
||||
$scope.$on("$ionicView.enter", onEnter);
|
||||
|
||||
|
|
@ -31,10 +20,6 @@ angular
|
|||
sendFlowService.state.pop();
|
||||
}
|
||||
|
||||
// Init before entering on this screen
|
||||
initVariables();
|
||||
// Then start
|
||||
|
||||
$scope.params = sendFlowService.state.getClone();
|
||||
|
||||
console.log('walletSelector onBeforeEnter after back sendflow', $scope.params);
|
||||
|
|
@ -67,6 +52,9 @@ angular
|
|||
$scope.specificAmount = $scope.specificAlternativeAmount = '';
|
||||
$scope.isPaymentRequest = true;
|
||||
}
|
||||
if ($scope.params.thirdParty) {
|
||||
$scope.thirdParty = $scope.params.thirdParty;
|
||||
}
|
||||
};
|
||||
|
||||
function onEnter (event, data) {
|
||||
|
|
@ -74,7 +62,7 @@ angular
|
|||
$scope.selectedPriceDisplay = config.wallet.settings.priceDisplay;
|
||||
});
|
||||
|
||||
if ($scope.params.thirdParty) {
|
||||
if ($scope.thirdParty) {
|
||||
// Third party services specific logic
|
||||
handleThirdPartyIfShapeshift();
|
||||
}
|
||||
|
|
@ -114,8 +102,8 @@ angular
|
|||
}
|
||||
|
||||
function handleThirdPartyIfShapeshift() {
|
||||
console.log($scope.params.thirdParty, $scope.coin);
|
||||
if ($scope.params.thirdParty.id === 'shapeshift' && $scope.type === 'destination') { // Shapeshift wants to know the
|
||||
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';
|
||||
|
|
@ -129,8 +117,6 @@ angular
|
|||
var walletsAll = [];
|
||||
var walletsSufficientFunds = [];
|
||||
$scope.walletsInsufficientFunds = []; // For origin screen
|
||||
$scope.walletsBtc = [];
|
||||
$scope.walletsBch = [];
|
||||
|
||||
if ($scope.type === 'origin') {
|
||||
$scope.headerTitle = gettextCatalog.getString('Choose a wallet to send from');
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ angular.module('copayApp.directives')
|
|||
scope.type = "url";
|
||||
} else if (scope.data.parsed.publicAddress) {
|
||||
scope.type = "bitcoinAddress";
|
||||
var prefix = scope.data.coin === 'bch' ? (scope.data.parsed.isTestnet ? 'bchtest:' : 'bitcoincash:') : '';
|
||||
scope.data.toAddress = (scope.data.parsed.publicAddress.cashAddr ? prefix + scope.data.parsed.publicAddress.cashAddr : false) || scope.data.parsed.publicAddress.legacy || scope.data.parsed.publicAddress.bitpay;
|
||||
var prefix = scope.data.parsed.isTestnet ? 'bchtest:' : 'bitcoincash:';
|
||||
scope.data.toAddress = (prefix + scope.data.parsed.publicAddress.cashAddr) || scope.data.parsed.publicAddress.legacy || scope.data.parsed.publicAddress.bitpay;
|
||||
} else {
|
||||
scope.type = "text";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,6 @@
|
|||
hasPassphrase: false,
|
||||
type: 1,
|
||||
},
|
||||
isTestnet: false,
|
||||
isValid: false,
|
||||
label: '',
|
||||
message: '',
|
||||
|
|
@ -171,6 +170,7 @@
|
|||
"req-param0": '',
|
||||
"req-param1": ''
|
||||
},
|
||||
testnet: false,
|
||||
url: '' // For BIP70
|
||||
}
|
||||
|
||||
|
|
@ -204,13 +204,7 @@
|
|||
|
||||
} else if (/^(?:bitcoincash)|(?:bitcoin-cash)$/.test(preColonLower)) {
|
||||
parsed.coin = 'bch';
|
||||
parsed.isTestnet = false;
|
||||
addressAndParams = colonSplit[2].trim();
|
||||
console.log('Is bch');
|
||||
|
||||
} else if (/^(?:bch)$/.test(preColonLower)) {
|
||||
parsed.coin = 'bch';
|
||||
parsed.isTestnet = false;
|
||||
parsed.test = false;
|
||||
addressAndParams = colonSplit[2].trim();
|
||||
console.log('Is bch');
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ describe('bitcoinUriService', function() {
|
|||
expect(parsed.isValid).toBe(true);
|
||||
expect(parsed.coin).toBe('bch');
|
||||
expect(parsed.publicAddress).toBeUndefined();
|
||||
expect(parsed.isTestnet).toBe(false);
|
||||
expect(parsed.isTestnet).toBeUndefined();
|
||||
expect(parsed.url).toBe('https://bitpay.com/i/SmHdie5dvBnG5kouZzEPzu');
|
||||
});
|
||||
|
||||
|
|
@ -171,16 +171,6 @@ describe('bitcoinUriService', function() {
|
|||
expect(parsed.isTestnet).toBe(false);
|
||||
});
|
||||
|
||||
it('legacy address with bch prefix', function() {
|
||||
|
||||
var parsed = bitcoinUriService.parse('bch:19yUdM2H7sADrabR6Afu9zTpmwqr6WYprX');
|
||||
|
||||
expect(parsed.isValid).toBe(true);
|
||||
expect(parsed.coin).toBe('bch');
|
||||
expect(parsed.publicAddress.legacy).toBe('19yUdM2H7sADrabR6Afu9zTpmwqr6WYprX');
|
||||
expect(parsed.isTestnet).toBe(false);
|
||||
});
|
||||
|
||||
it('cashAddr testnet with prefix', function() {
|
||||
|
||||
var parsed = bitcoinUriService.parse('bchtest:qpcz6pmurq9ctg5848trzz9zmuuygj4q5qam7ph3gt');
|
||||
|
|
@ -201,16 +191,6 @@ describe('bitcoinUriService', function() {
|
|||
expect(parsed.isTestnet).toBe(false);
|
||||
});
|
||||
|
||||
it('cashAddr with bch prefix', function() {
|
||||
|
||||
var parsed = bitcoinUriService.parse('bch:qpqzqtjqqc00nsxj0e3kevz65ujg4yt5z5w99jap5f');
|
||||
|
||||
expect(parsed.isValid).toBe(true);
|
||||
expect(parsed.coin).toBe('bch');
|
||||
expect(parsed.publicAddress.cashAddr).toBe('qpqzqtjqqc00nsxj0e3kevz65ujg4yt5z5w99jap5f');
|
||||
expect(parsed.isTestnet).toBe(false);
|
||||
});
|
||||
|
||||
it('cashAddr with dash', function() {
|
||||
|
||||
var parsed = bitcoinUriService.parse('bitcoin-cash:qpshfu3dk5s3e7zdcgdcun6xgxtra6uyxs7g580js0');
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
|||
},
|
||||
|
||||
bwscash: {
|
||||
url: 'https://bwscash.bitcoin.com/bws/api'
|
||||
url: 'https://bws.freepages.dk/bws/api'
|
||||
},
|
||||
|
||||
download: {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
|
|||
normal: gettext('Normal'),
|
||||
economy: gettext('Economy'),
|
||||
superEconomy: gettext('Super Economy'),
|
||||
// free: gettext('No fee (works only for BCH)'),
|
||||
custom: gettext('Custom')
|
||||
};
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
|
|||
root.getFeeRate = function(coin, network, feeLevel, cb) {
|
||||
|
||||
if (feeLevel == 'custom') return cb();
|
||||
// if (feeLevel == 'free') return cb(null, 0);
|
||||
|
||||
network = network || 'livenet';
|
||||
|
||||
|
|
@ -48,9 +50,9 @@ angular.module('copayApp.services').factory('feeService', function($log, $timeou
|
|||
})
|
||||
});
|
||||
}
|
||||
|
||||
var feeRate = feeLevelRate.feePerKb;
|
||||
|
||||
|
||||
if (!fromCache) $log.debug('Dynamic fee: ' + feeLevel + '/' + network + ' ' + (feeLevelRate.feePerKb / 1000).toFixed() + ' SAT/B');
|
||||
|
||||
return cb(null, feeRate);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ angular
|
|||
// Detect some merchant that we know
|
||||
if (payProData.memo.indexOf('eGifter') > -1) {
|
||||
name = 'eGifter'
|
||||
} else if (payProData.url.indexOf('https://bitpay.com') > -1) {
|
||||
} else if (paymentUrl.indexOf('https://bitpay.com') > -1) {
|
||||
name = 'BitPay';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ angular.module('copayApp.services').factory('servicesService', function(configSe
|
|||
name: 'shapeshift',
|
||||
title: 'Shapeshift',
|
||||
icon: 'icon-shapeshift',
|
||||
sref: 'tabs.shapeshift',
|
||||
href: 'https://shapeshift.io/'
|
||||
}];
|
||||
|
||||
root.register = function(serviceInfo) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ var ShapeShift = (function() {
|
|||
var parsedResponse = JP(xmlhttp.responseText);
|
||||
cb.apply(null, [parsedResponse]);
|
||||
} else {
|
||||
cb.apply(null, [new Error('Request Failed')]);
|
||||
cb.apply(null, [new Error('Request Failed')])
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ angular
|
|||
} else if (amount > service.marketData.maxLimit) {
|
||||
cb(new Error(gettextCatalog.getString('Amount is above the limit')));
|
||||
} else {
|
||||
|
||||
// Init service data
|
||||
service.withdrawalAddress = withdrawalAddress;
|
||||
service.returnAddress = returnAddress;
|
||||
|
|
@ -73,7 +72,7 @@ angular
|
|||
|
||||
// Check the address
|
||||
shapeshiftApiService
|
||||
.ValidateAddress(returnAddress, coinOut)
|
||||
.ValidateAddress(withdrawalAddress, coinOut)
|
||||
.then(function onSuccess(response) {
|
||||
if (response && response.isvalid) {
|
||||
// Prepare the transaction shapeshift side
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
.factory('walletHistoryService', walletHistoryService);
|
||||
|
||||
function walletHistoryService(configService, storageService, lodash, $log, txFormatService) {
|
||||
var PAGE_SIZE = 50;
|
||||
//var PAGE_SIZE = 20; // For dev only
|
||||
//var PAGE_SIZE = 50;
|
||||
var PAGE_SIZE = 20; // For dev only
|
||||
// How much to overlap on each end of the page, for mitigating inconsistent sort order.
|
||||
var PAGE_OVERLAP_FRACTION = 0.2;
|
||||
var PAGE_OVERLAP = Math.floor(PAGE_SIZE * PAGE_OVERLAP_FRACTION);
|
||||
|
|
@ -28,8 +28,8 @@
|
|||
function addEarlyTransactions(walletId, cachedTxs, newTxs) {
|
||||
|
||||
var cachedTxIndexFromId = {};
|
||||
cachedTxs.forEach(function forCachedTx(tx, txIndex){
|
||||
cachedTxIndexFromId[tx.txid] = txIndex;
|
||||
cachedTxs.forEach(function forCachedTx(tx){
|
||||
cachedTxIndexFromId[tx.txid] = true;
|
||||
});
|
||||
|
||||
var confirmationsUpdated = false;
|
||||
|
|
@ -49,9 +49,8 @@
|
|||
|
||||
var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
|
||||
console.log('overlappingTxFraction:', overlappingTxFraction);
|
||||
console.log('overlappingTxsCount:', overlappingTxsCount);
|
||||
|
||||
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION || (someTransactionsWereNew && overlappingTxsCount === 0)) { // We are good
|
||||
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
|
||||
if (someTransactionsWereNew) {
|
||||
saveTxHistory(walletId, cachedTxs);
|
||||
} else if (confirmationsUpdated) {
|
||||
|
|
@ -94,7 +93,7 @@
|
|||
|
||||
var overlappingTxFraction = overlappingTxsCount / Math.min(cachedTxs.length, PAGE_OVERLAP);
|
||||
|
||||
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION || (someTransactionsWereNew && overlappingTxsCount === 0)) { // We are good
|
||||
if (overlappingTxFraction >= MIN_KNOWN_TX_OVERLAP_FRACTION) { // We are good
|
||||
if (someTransactionsWereNew) {
|
||||
var allTxs = uniqueNewTxs.concat(cachedTxs);
|
||||
saveTxHistory(walletId, allTxs);
|
||||
|
|
@ -148,6 +147,7 @@
|
|||
* @param {function(error, txs)} cb - txs is always an array, may be empty
|
||||
*/
|
||||
function getCachedTxHistory(walletId, cb) {
|
||||
console.log('txhistory updateLocalTxHistoryByPage()');
|
||||
storageService.getTxHistory(walletId, function onGetTxHistory(err, txHistoryString){
|
||||
if (err) {
|
||||
return cb(err, []);
|
||||
|
|
@ -230,6 +230,7 @@
|
|||
}
|
||||
|
||||
function updateLocalTxHistoryByPage(wallet, getLatest, flushCacheOnNew, cb) {
|
||||
console.log('txhistory updaetLocalTxHistoryByPage()');
|
||||
if (flushCacheOnNew) {
|
||||
fetchTxHistoryByPage(wallet, 0, function onFetchTxHistory(err, txs){
|
||||
if (err) {
|
||||
|
|
|
|||
209
www/css/main.css
209
www/css/main.css
|
|
@ -10083,7 +10083,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
|
|||
.onboarding .button.button-white.button-standard,
|
||||
.onboarding .button.button-green.button-standard,
|
||||
.onboarding .button.button-assertive.button-standard, #shapeshift .button-shapeshift {
|
||||
width: 85%;
|
||||
width: 90%;
|
||||
max-width: 300px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
|
@ -10195,6 +10195,7 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
|
|||
.button {
|
||||
border-radius: 6px; }
|
||||
.button.button-full {
|
||||
border-radius: 0;
|
||||
display: block; }
|
||||
.button-green {
|
||||
border-color: #FFF;
|
||||
|
|
@ -10263,8 +10264,8 @@ ion-view.deflash-blue:before, ion-view#view-amount:before, ion-view#view-confirm
|
|||
color: #FFFFFF;
|
||||
text-decoration: none; }
|
||||
.button-white-outline.active, .button-white-outline.activated {
|
||||
border-color: #FFF;
|
||||
background-color: #FAFAFA; }
|
||||
border-color: #FFFFFF;
|
||||
background-color: #FFFFFF; }
|
||||
.button-white-outline.button-outline {
|
||||
border-color: #FFFFFF;
|
||||
background: transparent;
|
||||
|
|
@ -10632,7 +10633,7 @@ qrcode {
|
|||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.long .primary-amount-display {
|
||||
font-size: 2em; } }
|
||||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
|
||||
font-size: 0.9em; }
|
||||
font-size: 1.2em; }
|
||||
@media (min-width: 375px) {
|
||||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long input, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .unit, #view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount.very-long .primary-amount-display {
|
||||
font-size: 1.3em; } }
|
||||
|
|
@ -10659,7 +10660,8 @@ qrcode {
|
|||
line-height: 1em; }
|
||||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .primary-amount .primary-amount-display {
|
||||
margin-right: 5px;
|
||||
word-break: break-all; }
|
||||
word-break: break-all;
|
||||
width: 100%; }
|
||||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .alternative-amount {
|
||||
color: #6F6F70; }
|
||||
#view-amount .scroll-content .send-amount .send-amount-tool .send-amount-tool-input .switch-currencies {
|
||||
|
|
@ -10680,30 +10682,33 @@ qrcode {
|
|||
justify-content: space-between;
|
||||
margin: 0 14px; }
|
||||
#view-amount .scroll-content .send-amount-extras .available-funds {
|
||||
color: #6F6F70; }
|
||||
color: #6F6F70;
|
||||
text-align: left; }
|
||||
#view-amount .scroll-content .send-amount-extras .change-currency {
|
||||
text-align: right; }
|
||||
#view-amount .scroll-content .send-amount-extras .warning {
|
||||
color: #b7664d; }
|
||||
#view-amount .scroll-content .send-amount-extras .extra,
|
||||
#view-amount .scroll-content .send-amount-extras button.extra {
|
||||
/*display: flex;*/
|
||||
flex: 0 1 auto; }
|
||||
#view-amount .scroll-content .send-amount-extras 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; }
|
||||
#view-amount .scroll-content .send-amount-extras .button .icon:before {
|
||||
font-size: 14px;
|
||||
#view-amount .scroll-content .send-amount-extras .extra {
|
||||
flex: 1;
|
||||
line-height: normal; }
|
||||
#view-amount .scroll-content .send-amount-extras .button span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; }
|
||||
#view-amount .scroll-content .send-amount-extras .extra .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; }
|
||||
#view-amount .scroll-content .send-amount-extras .extra .button .icon:before {
|
||||
font-size: 14px;
|
||||
line-height: normal; }
|
||||
#view-amount .scroll-content .send-amount-extras .extra .button span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center; }
|
||||
#view-amount .scroll-content .button.no-margin {
|
||||
margin: 0; }
|
||||
#view-amount .scroll-content .notification-warning {
|
||||
|
|
@ -10939,14 +10944,14 @@ qrcode {
|
|||
#tab-home .buttons .button {
|
||||
border: 2px solid;
|
||||
border-radius: 47px;
|
||||
padding: 0 15px 0 15px;
|
||||
padding: 8px 2px 8px 2px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-size: 19px;
|
||||
font-weight: bolder;
|
||||
min-height: auto;
|
||||
line-height: 36px; }
|
||||
min-height: 0;
|
||||
line-height: 19px; }
|
||||
|
||||
#tab-home .wallet-coin-logo {
|
||||
vertical-align: middle;
|
||||
|
|
@ -11013,6 +11018,10 @@ qrcode {
|
|||
#tab-home .release .title {
|
||||
font-weight: 700;
|
||||
color: #444; }
|
||||
#tab-home .release .release-notes {
|
||||
white-space: pre;
|
||||
white-space: pre-line;
|
||||
text-align: left; }
|
||||
#tab-home .release .button {
|
||||
width: 100%;
|
||||
border: none; }
|
||||
|
|
@ -11025,6 +11034,14 @@ qrcode {
|
|||
#tab-home .badge {
|
||||
top: 11px; }
|
||||
|
||||
.popup-update .popup-buttons {
|
||||
display: block; }
|
||||
|
||||
.popup-update .popup-buttons .button {
|
||||
display: block;
|
||||
min-width: 100% !important;
|
||||
margin-top: 4px; }
|
||||
|
||||
#tab-receive .button-share {
|
||||
color: #fff;
|
||||
box-shadow: none;
|
||||
|
|
@ -11101,6 +11118,8 @@ qrcode {
|
|||
|
||||
#tab-receive .payment-received-container {
|
||||
margin: 0 20px; }
|
||||
#tab-receive .payment-received-container svg {
|
||||
max-height: 400px; }
|
||||
#tab-receive .payment-received-container .payment-received-amount {
|
||||
font-size: 1.8em;
|
||||
display: block;
|
||||
|
|
@ -11207,7 +11226,7 @@ qrcode {
|
|||
margin: auto;
|
||||
margin-top: 18px; }
|
||||
#tab-send .send-wrapper .buttons .button {
|
||||
height: 60px;
|
||||
min-height: 65px;
|
||||
line-height: 16px;
|
||||
margin-right: 0px;
|
||||
width: 95%;
|
||||
|
|
@ -11284,7 +11303,9 @@ qrcode {
|
|||
margin-top: 18px; }
|
||||
#tab-send .sendTip .buttons .button {
|
||||
font-weight: bold;
|
||||
font-size: 19px; }
|
||||
font-size: 19px;
|
||||
line-height: 26px;
|
||||
padding: 8px 6px; }
|
||||
#tab-send .sendTip .button-first-contact img {
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
|
|
@ -11920,6 +11941,13 @@ qrcode {
|
|||
#walletDetails .bp-content.status-bar {
|
||||
margin-top: 20px;
|
||||
margin-top: env(safe-area-inset-top); }
|
||||
#walletDetails .bp-content.collapse ion-content {
|
||||
margin-top: 40px; }
|
||||
#walletDetails .bp-content.collapse .amount__scale, #walletDetails .bp-content.collapse .amount__error {
|
||||
-webkit-transform: scale3d(0.5, 0.5, 0.5) translateY(0px);
|
||||
transform: scale3d(0.5, 0.5, 0.5) translateY(0px); }
|
||||
#walletDetails .bp-content.collapse .amount-alternative, #walletDetails .bp-content.collapse .send-receive-buttons, #walletDetails .bp-content.collapse .wallet-details-wallet-info {
|
||||
opacity: 0; }
|
||||
#walletDetails .bar-header {
|
||||
border: 0;
|
||||
background: #eeb640; }
|
||||
|
|
@ -11932,9 +11960,12 @@ qrcode {
|
|||
#walletDetails ion-content {
|
||||
padding-top: 0;
|
||||
top: 0;
|
||||
transition: all 0.25s ease-in-out;
|
||||
margin-top: 185px;
|
||||
margin-bottom: 16px; }
|
||||
#walletDetails ion-content.collapsible {
|
||||
margin-top: 230px; }
|
||||
@media only screen and (max-height: 500px) {
|
||||
#walletDetails ion-content {
|
||||
margin-top: 165px; } }
|
||||
#walletDetails ion-content .scroll {
|
||||
background: #f8f8f9;
|
||||
min-height: 300px; }
|
||||
|
|
@ -11965,38 +11996,45 @@ qrcode {
|
|||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 20px; }
|
||||
bottom: 20px;
|
||||
transition: all 0.25s ease-in-out; }
|
||||
#walletDetails .amount-wrapper .send-receive-buttons > .col {
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 0; }
|
||||
#walletDetails .amount-wrapper .send-receive-buttons .button {
|
||||
border: 2px solid;
|
||||
border-radius: 47px;
|
||||
padding: 0 15px 0 15px;
|
||||
padding: 6px 2px 6px 2px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
font-size: 19px;
|
||||
font-weight: bolder;
|
||||
min-height: auto;
|
||||
line-height: 36px; }
|
||||
min-height: 0;
|
||||
line-height: 19px; }
|
||||
#walletDetails .amount {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
height: 230px;
|
||||
padding-top: 40px;
|
||||
display: block;
|
||||
align-items: center;
|
||||
justify-content: center; }
|
||||
color: #fff;
|
||||
display: block;
|
||||
height: 230px;
|
||||
justify-content: center;
|
||||
padding-top: 40px;
|
||||
text-align: center;
|
||||
transition: all 0.25s ease-in-out;
|
||||
width: 100%; }
|
||||
@media only screen and (max-height: 500px) {
|
||||
#walletDetails .amount {
|
||||
height: 210px; } }
|
||||
#walletDetails .amount__balance {
|
||||
-webkit-transform: scale3d(1, 1, 1) translateY(45px);
|
||||
transform: scale3d(1, 1, 1) translateY(45px); }
|
||||
transform: scale3d(1, 1, 1) translateY(45px);
|
||||
transition: all 0.25s ease-in-out; }
|
||||
#walletDetails .amount__updating {
|
||||
z-index: 999;
|
||||
margin-top: -2.1rem; }
|
||||
#walletDetails .amount-alternative {
|
||||
line-height: 36px; }
|
||||
line-height: 36px;
|
||||
transition: all 0.25s ease-in-out; }
|
||||
#walletDetails .amount__button-balance {
|
||||
background-color: transparent;
|
||||
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
|
|
@ -12006,7 +12044,8 @@ qrcode {
|
|||
vertical-align: middle; }
|
||||
#walletDetails .amount__error {
|
||||
font-size: 14px;
|
||||
padding: 35px 20px; }
|
||||
padding: 35px 20px;
|
||||
opacity: 1; }
|
||||
#walletDetails .no-alternative {
|
||||
padding-top: 45px; }
|
||||
#walletDetails .item.item-footer {
|
||||
|
|
@ -12071,7 +12110,9 @@ a.item {
|
|||
font-size: 0.9em; }
|
||||
|
||||
.loading-wallet svg {
|
||||
margin-top: 0; }
|
||||
margin-top: 0;
|
||||
width: 16px;
|
||||
height: 16px; }
|
||||
|
||||
#advanced-settings .list .item:before {
|
||||
display: block;
|
||||
|
|
@ -12125,7 +12166,9 @@ a.item {
|
|||
margin-top: 18px; }
|
||||
#shapeshift .empty-case .buttons .button {
|
||||
font-weight: bold;
|
||||
font-size: 19px; }
|
||||
font-size: 19px;
|
||||
line-height: 26px;
|
||||
padding: 8px 6px; }
|
||||
#shapeshift .empty-case .button-first-contact img {
|
||||
height: 19px;
|
||||
width: 19px;
|
||||
|
|
@ -13812,6 +13855,7 @@ click-to-accept {
|
|||
height: 92px;
|
||||
width: 100%; }
|
||||
click-to-accept .click-to-accept__button.button.button-primary.button-standard {
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
max-width: 9999px;
|
||||
width: 100%; }
|
||||
|
|
@ -13918,6 +13962,8 @@ slide-to-accept {
|
|||
height: 92px;
|
||||
width: 100%;
|
||||
background: #494949; }
|
||||
slide-to-accept .slide .button {
|
||||
border-radius: 0; }
|
||||
slide-to-accept .slide__listener {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
|
@ -15349,20 +15395,25 @@ log-options #check-bar .checkbox-icon {
|
|||
#cash-scan a {
|
||||
cursor: pointer; }
|
||||
|
||||
#view-review {
|
||||
background-color: #494949; }
|
||||
#view-review slide-to-accept, #view-review slide-to-accept-success {
|
||||
margin-bottom: constant(safe-area-inset-bottom);
|
||||
/* iOS 11.0 */
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
/* iOS 11.2 */ }
|
||||
#view-review .fee-summary {
|
||||
position: absolute;
|
||||
bottom: 92px; }
|
||||
#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
|
||||
box-shadow: none; }
|
||||
#view-review .warning {
|
||||
color: #b7664d; }
|
||||
#view-review slide-to-accept, #view-review slide-to-accept-success {
|
||||
margin-bottom: constant(safe-area-inset-bottom);
|
||||
/* iOS 11.0 */
|
||||
margin-bottom: env(safe-area-inset-bottom);
|
||||
/* iOS 11.2 */ }
|
||||
|
||||
#view-review .fee-summary {
|
||||
bottom: 92px;
|
||||
bottom: calc(92px + constant(safe-area-inset-bottom));
|
||||
/* iOS 11.0 */
|
||||
bottom: calc(92px + env(safe-area-inset-bottom));
|
||||
/* iOS 11.2 */
|
||||
position: absolute; }
|
||||
|
||||
#view-review .shapeshift-banner, #view-review .bitpay-banner, #view-review .egifter-banner {
|
||||
box-shadow: none; }
|
||||
|
||||
#view-review .warning {
|
||||
color: #b7664d; }
|
||||
|
||||
.gravatar {
|
||||
border-radius: 3px;
|
||||
|
|
@ -15395,6 +15446,7 @@ log-options #check-bar .checkbox-icon {
|
|||
left: 13px;
|
||||
top: 50%;
|
||||
padding: 0;
|
||||
-webkit-transform: translate(0, -50%);
|
||||
transform: translate(0, -50%); }
|
||||
|
||||
.item .item-content.item-content-compact {
|
||||
|
|
@ -15463,8 +15515,8 @@ ion-content.padded-bottom-cta-with-summary {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis; }
|
||||
.address-frame.expanded {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all; }
|
||||
white-space: normal;
|
||||
text-overflow: clip; }
|
||||
.address-frame .prefix {
|
||||
color: #000000; }
|
||||
.address-frame .mid {
|
||||
|
|
@ -15507,13 +15559,13 @@ ion-content.padded-bottom-cta-with-summary {
|
|||
transform: scale(1, 1); }
|
||||
|
||||
.fee-summary {
|
||||
position: relative;
|
||||
background-color: #F2F2F2;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
padding: 5px 12px 15px;
|
||||
box-sizing: border-box;
|
||||
background-color: #F2F2F2; }
|
||||
position: relative;
|
||||
width: 100%; }
|
||||
.fee-summary:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
|
|
@ -15523,16 +15575,16 @@ ion-content.padded-bottom-cta-with-summary {
|
|||
height: 15px;
|
||||
background: linear-gradient(to bottom, rgba(242, 242, 242, 0) 0%, #f2f2f2 100%); }
|
||||
.fee-summary .amount {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%; }
|
||||
.fee-summary .amount .fee-fiat.positive {
|
||||
color: #70955F; }
|
||||
.fee-summary .amount .fee-fiat.negative {
|
||||
color: #C24633; }
|
||||
.fee-summary .amount .fee-fiat {
|
||||
display: inline; }
|
||||
.fee-summary .amount .fee-fiat.positive {
|
||||
color: #70955F; }
|
||||
.fee-summary .amount .fee-fiat.negative {
|
||||
color: #C24633; }
|
||||
.fee-summary .amount .fee-crypto {
|
||||
color: #A7A7A7; }
|
||||
color: #A7A7A7;
|
||||
float: right; }
|
||||
|
||||
.formatted-amount {
|
||||
display: inline-block; }
|
||||
|
|
@ -15557,6 +15609,9 @@ ion-content.padded-bottom-cta-with-summary {
|
|||
margin-left: 5px;
|
||||
text-transform: uppercase; }
|
||||
|
||||
.wallet-balance-directive {
|
||||
display: inline-block; }
|
||||
|
||||
/* This is for rules that don't yet have a home.
|
||||
* Our goal is to delete this file. Search the regex: /class=".*CLASS.*?"/
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,7 +6,14 @@
|
|||
</div>
|
||||
<div ng-show="!hide">
|
||||
<div ng-repeat="service in services track by $index">
|
||||
<a ui-sref="{{service.sref}}" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||
<a ng-if="service.sref" ui-sref="{{service.sref}}" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||
<i class="icon big-icon-svg theme-circle theme-circle-services">
|
||||
<div class="bg {{service.icon}}"></div>
|
||||
</i>
|
||||
<span>{{service.title || service.name}}</span>
|
||||
<i class="icon bp-arrow-right"></i>
|
||||
</a>
|
||||
<a ng-if="!service.sref" ng-click="open('{{service.href}}')" id="home_{{service.name}}" title="{{service.title || service.name}}" class="track_link_click_out item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||
<i class="icon big-icon-svg theme-circle theme-circle-services">
|
||||
<div class="bg {{service.icon}}"></div>
|
||||
</i>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue