Coinbase: first step integration, connect account and main view
This commit is contained in:
parent
eea32d3069
commit
af932b3e59
8 changed files with 466 additions and 244 deletions
|
|
@ -1,25 +1,53 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('coinbaseController',
|
angular.module('copayApp.controllers').controller('coinbaseController', function($rootScope, $scope, $timeout, $ionicModal, $log, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess, popupService, gettextCatalog, externalLinkService) {
|
||||||
function($rootScope, $scope, $timeout, $ionicModal, profileService, configService, storageService, coinbaseService, lodash, platformInfo, ongoingProcess) {
|
|
||||||
|
|
||||||
var isNW = platformInfo.isNW;
|
var isNW = platformInfo.isNW;
|
||||||
|
|
||||||
if (platformInfo.isCordova && StatusBar.isVisible) {
|
var init = function() {
|
||||||
StatusBar.backgroundColorByHexString("#4B6178");
|
ongoingProcess.set('connectingCoinbase', true);
|
||||||
}
|
coinbaseService.init($scope.accessToken, function(err, data) {
|
||||||
|
console.log('[coinbase.js:9]',data); //TODO)
|
||||||
|
ongoingProcess.set('connectingCoinbase', false);
|
||||||
|
if (err || lodash.isEmpty(data)) {
|
||||||
|
if (err) {
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Updating accessToken and accountId
|
||||||
|
$timeout(function() {
|
||||||
|
$scope.accessToken = data.accessToken;
|
||||||
|
$scope.accountId = data.accountId;
|
||||||
|
$scope.updateTransactions();
|
||||||
|
$scope.$apply();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.openAuthenticateWindow = function() {
|
$scope.updateTransactions = function() {
|
||||||
var oauthUrl = this.getAuthenticateUrl();
|
$log.debug('Checking for transactions...');
|
||||||
if (!isNW) {
|
coinbaseService.getPendingTransactions($scope.accessToken, $scope.accountId, function(err, txs) {
|
||||||
$rootScope.openExternalLink(oauthUrl, '_system');
|
console.log('[coinbase.js:43]',txs); //TODO)
|
||||||
} else {
|
$scope.pendingTransactions = txs;
|
||||||
var self = this;
|
});
|
||||||
var gui = require('nw.gui');
|
|
||||||
var win = gui.Window.open(oauthUrl, {
|
};
|
||||||
focus: true,
|
|
||||||
position: 'center'
|
this.openAuthenticateWindow = function() {
|
||||||
});
|
var oauthUrl = this.getAuthenticateUrl();
|
||||||
|
externalLinkService.open(oauthUrl);
|
||||||
|
/*
|
||||||
|
* Not working (NW bug)
|
||||||
|
if (!isNW) {
|
||||||
|
externalLinkService.open(oauthUrl);
|
||||||
|
} else {
|
||||||
|
var self = this;
|
||||||
|
var gui = require('nw.gui');
|
||||||
|
gui.Window.open(oauthUrl, {
|
||||||
|
focus: true,
|
||||||
|
position: 'center'
|
||||||
|
}, function(win) {
|
||||||
win.on('loaded', function() {
|
win.on('loaded', function() {
|
||||||
var title = win.title;
|
var title = win.title;
|
||||||
if (title.indexOf('Coinbase') == -1) {
|
if (title.indexOf('Coinbase') == -1) {
|
||||||
|
|
@ -28,51 +56,47 @@ angular.module('copayApp.controllers').controller('coinbaseController',
|
||||||
win.close();
|
win.close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getAuthenticateUrl = function() {
|
|
||||||
return coinbaseService.getOauthCodeUrl();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.submitOauthCode = function(code) {
|
|
||||||
var self = this;
|
|
||||||
var coinbaseTestnet = configService.getSync().coinbase.testnet;
|
|
||||||
var network = coinbaseTestnet ? 'testnet' : 'livenet';
|
|
||||||
ongoingProcess.set('connectingCoinbase', true);
|
|
||||||
this.error = null;
|
|
||||||
$timeout(function() {
|
|
||||||
coinbaseService.getToken(code, function(err, data) {
|
|
||||||
ongoingProcess.set('connectingCoinbase', false);
|
|
||||||
if (err) {
|
|
||||||
self.error = err;
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.$apply();
|
|
||||||
}, 100);
|
|
||||||
} else if (data && data.access_token && data.refresh_token) {
|
|
||||||
storageService.setCoinbaseToken(network, data.access_token, function() {
|
|
||||||
storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() {
|
|
||||||
$scope.$emit('Local/CoinbaseUpdated', data.access_token);
|
|
||||||
$timeout(function() {
|
|
||||||
$scope.$apply();
|
|
||||||
}, 100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, 100);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.openTxModal = function(tx) {
|
|
||||||
$scope.tx = tx;
|
|
||||||
|
|
||||||
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
|
||||||
scope: $scope,
|
|
||||||
animation: 'slide-in-up'
|
|
||||||
}).then(function(modal) {
|
|
||||||
$scope.coinbaseTxDetailsModal = modal;
|
|
||||||
$scope.coinbaseTxDetailsModal.show();
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getAuthenticateUrl = function() {
|
||||||
|
return coinbaseService.getOauthCodeUrl();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.submitOauthCode = function(code) {
|
||||||
|
var self = this;
|
||||||
|
ongoingProcess.set('connectingCoinbase', true);
|
||||||
|
$scope.error = null;
|
||||||
|
$timeout(function() {
|
||||||
|
coinbaseService.getToken(code, function(err, accessToken) {
|
||||||
|
ongoingProcess.set('connectingCoinbase', false);
|
||||||
|
if (err) {
|
||||||
|
popupService.showAlert(gettextCatalog.getString('Error'), err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$scope.accessToken = accessToken;
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.openTxModal = function(tx) {
|
||||||
|
$scope.tx = tx;
|
||||||
|
|
||||||
|
$ionicModal.fromTemplateUrl('views/modals/coinbase-tx-details.html', {
|
||||||
|
scope: $scope,
|
||||||
|
animation: 'slide-in-up'
|
||||||
|
}).then(function(modal) {
|
||||||
|
$scope.coinbaseTxDetailsModal = modal;
|
||||||
|
$scope.coinbaseTxDetailsModal.show();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||||
|
coinbaseService.setCredentials();
|
||||||
|
$scope.network = coinbaseService.getEnvironment();
|
||||||
|
init();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -926,21 +926,39 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.state('coinbase', {
|
.state('tabs.buyandsell.coinbase', {
|
||||||
url: '/coinbase',
|
url: '/coinbase',
|
||||||
templateUrl: 'views/coinbase.html'
|
views: {
|
||||||
|
'tab-home@tabs': {
|
||||||
|
controller: 'coinbaseController',
|
||||||
|
controllerAs: 'coinbase',
|
||||||
|
templateUrl: 'views/coinbase.html'
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.state('preferencesCoinbase', {
|
.state('tabs.buyandsell.coinbase.preferences', {
|
||||||
url: '/preferencesCoinbase',
|
url: '/preferences',
|
||||||
templateUrl: 'views/preferencesCoinbase.html'
|
'tab-home@tabs': {
|
||||||
|
controller: 'preferencesCoinbaseController',
|
||||||
|
controllerAs: 'coinbase',
|
||||||
|
templateUrl: 'views/preferencesCoinbase.html'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.state('buyCoinbase', {
|
.state('tabs.buyandsell.coinbase.buy', {
|
||||||
url: '/buycoinbase',
|
url: '/buy',
|
||||||
templateUrl: 'views/buyCoinbase.html'
|
'tab-home@tabs': {
|
||||||
|
controller: 'buyCoinbaseController',
|
||||||
|
controllerAs: 'buy',
|
||||||
|
templateUrl: 'views/buyCoinbase.html'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.state('sellCoinbase', {
|
.state('tabs.buyandsell.coinbase.sell', {
|
||||||
url: '/sellcoinbase',
|
url: '/sell',
|
||||||
templateUrl: 'views/sellCoinbase.html'
|
'tab-home@tabs': {
|
||||||
|
controller: 'sellCoinbaseController',
|
||||||
|
controllerAs: 'sell',
|
||||||
|
templateUrl: 'views/sellCoinbase.html'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,26 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, platformInfo, lodash, storageService, configService) {
|
angular.module('copayApp.services').factory('coinbaseService', function($http, $log, $window, platformInfo, lodash, storageService, configService) {
|
||||||
var root = {};
|
var root = {};
|
||||||
var credentials = {};
|
var credentials = {};
|
||||||
var isCordova = platformInfo.isCordova;
|
var isCordova = platformInfo.isCordova;
|
||||||
|
var isNW = platformInfo.isNW;
|
||||||
|
|
||||||
root.setCredentials = function(network) {
|
root.setCredentials = function() {
|
||||||
|
|
||||||
|
if (!$window.externalServices || !$window.externalServices.coinbase) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coinbase = $window.externalServices.coinbase;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Development: 'testnet'
|
||||||
|
* Production: 'livenet'
|
||||||
|
*/
|
||||||
|
credentials.NETWORK = 'livenet';
|
||||||
|
|
||||||
|
// Coinbase permissions
|
||||||
credentials.SCOPE = ''
|
credentials.SCOPE = ''
|
||||||
+ 'wallet:accounts:read,'
|
+ 'wallet:accounts:read,'
|
||||||
+ 'wallet:addresses:read,'
|
+ 'wallet:addresses:read,'
|
||||||
|
|
@ -20,27 +35,45 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
+ 'wallet:transactions:send,'
|
+ 'wallet:transactions:send,'
|
||||||
+ 'wallet:payment-methods:read';
|
+ 'wallet:payment-methods:read';
|
||||||
|
|
||||||
if (isCordova) {
|
// NW has a bug with Window Object
|
||||||
credentials.REDIRECT_URI = 'copay://coinbase';
|
if (isCordova && isNW) {
|
||||||
|
credentials.REDIRECT_URI = coinbase.redirect_uri.mobile;
|
||||||
} else {
|
} else {
|
||||||
credentials.REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob';
|
credentials.REDIRECT_URI = coinbase.redirect_uri.desktop;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (network == 'testnet') {
|
if (credentials.NETWORK == 'testnet') {
|
||||||
credentials.HOST = 'https://sandbox.coinbase.com';
|
credentials.HOST = coinbase.sandbox.host;
|
||||||
credentials.API = 'https://api.sandbox.coinbase.com';
|
credentials.API = coinbase.sandbox.api;
|
||||||
credentials.CLIENT_ID = '6cdcc82d5d46654c46880e93ab3d2a43c639776347dd88022904bd78cd067841';
|
credentials.CLIENT_ID = coinbase.sandbox.client_id;
|
||||||
credentials.CLIENT_SECRET = '228cb6308951f4b6f41ba010c7d7981b2721a493c40c50fd2425132dcaccce59';
|
credentials.CLIENT_SECRET = coinbase.sandbox.client_secret;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
credentials.HOST = 'https://coinbase.com';
|
credentials.HOST = coinbase.production.host;
|
||||||
credentials.API = 'https://api.coinbase.com';
|
credentials.API = coinbase.production.api;
|
||||||
credentials.CLIENT_ID = window.coinbase_client_id;
|
credentials.CLIENT_ID = coinbase.production.client_id;
|
||||||
credentials.CLIENT_SECRET = window.coinbase_client_secret;
|
credentials.CLIENT_SECRET = coinbase.production.client_secret;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var _afterTokenReceived = function(data, cb) {
|
||||||
|
if (data && data.access_token && data.refresh_token) {
|
||||||
|
storageService.setCoinbaseToken(credentials.NETWORK, data.access_token, function() {
|
||||||
|
storageService.setCoinbaseRefreshToken(credentials.NETWORK, data.refresh_token, function() {
|
||||||
|
return cb(null, data.access_token);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return cb('Could not get the access token');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
root.getEnvironment = function() {
|
||||||
|
return credentials.NETWORK;
|
||||||
|
};
|
||||||
|
|
||||||
root.getOauthCodeUrl = function() {
|
root.getOauthCodeUrl = function() {
|
||||||
|
// TODO CHANGE LIMIT BACK TO 1000 *************************************************
|
||||||
return credentials.HOST
|
return credentials.HOST
|
||||||
+ '/oauth/authorize?response_type=code&client_id='
|
+ '/oauth/authorize?response_type=code&client_id='
|
||||||
+ credentials.CLIENT_ID
|
+ credentials.CLIENT_ID
|
||||||
|
|
@ -48,13 +81,13 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
+ credentials.REDIRECT_URI
|
+ credentials.REDIRECT_URI
|
||||||
+ '&state=SECURE_RANDOM&scope='
|
+ '&state=SECURE_RANDOM&scope='
|
||||||
+ credentials.SCOPE
|
+ credentials.SCOPE
|
||||||
+ '&meta[send_limit_amount]=1000&meta[send_limit_currency]=USD&meta[send_limit_period]=day';
|
+ '&meta[send_limit_amount]=1&meta[send_limit_currency]=USD&meta[send_limit_period]=day';
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getToken = function(code, cb) {
|
root.getToken = function(code, cb) {
|
||||||
var req = {
|
var req = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: credentials.API + '/oauth/token',
|
url: credentials.HOST + '/oauth/token',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
|
|
@ -71,18 +104,18 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
$http(req).then(function(data) {
|
$http(req).then(function(data) {
|
||||||
$log.info('Coinbase Authorization Access Token: SUCCESS');
|
$log.info('Coinbase Authorization Access Token: SUCCESS');
|
||||||
// Show pending task from the UI
|
// Show pending task from the UI
|
||||||
storageService.setNextStep('BuyAndSell', true, function(err) {});
|
storageService.setNextStep('BuyAndSell', 'true', function(err) {});
|
||||||
return cb(null, data.data);
|
_afterTokenReceived(data.data, cb);
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
$log.error('Coinbase Authorization Access Token: ERROR ' + data.statusText);
|
$log.error('Coinbase Authorization Access Token: ERROR ' + data.statusText);
|
||||||
return cb(data.data);
|
return cb(data.data || 'Could not get the access token');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.refreshToken = function(refreshToken, cb) {
|
var _refreshToken = function(refreshToken, cb) {
|
||||||
var req = {
|
var req = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: credentials.API + '/oauth/token',
|
url: credentials.HOST + '/oauth/token',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
|
|
@ -98,10 +131,63 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
|
|
||||||
$http(req).then(function(data) {
|
$http(req).then(function(data) {
|
||||||
$log.info('Coinbase Refresh Access Token: SUCCESS');
|
$log.info('Coinbase Refresh Access Token: SUCCESS');
|
||||||
return cb(null, data.data);
|
_afterTokenReceived(data.data, cb);
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
$log.error('Coinbase Refresh Access Token: ERROR ' + data.statusText);
|
$log.error('Coinbase Refresh Access Token: ERROR ' + data.statusText);
|
||||||
return cb(data.data);
|
return cb(data.data || 'Could not get the access token');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _getMainAccountId = function(accessToken, cb) {
|
||||||
|
root.getAccounts(accessToken, function(err, a) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
var data = a.data;
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].primary && data[i].type == 'wallet') {
|
||||||
|
return cb(null, data[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coinbaseService.logout(function() {});
|
||||||
|
return cb('Your primary account should be a WALLET. Set your wallet account as primary and try again');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
root.init = function(accessToken, cb) {
|
||||||
|
if (lodash.isEmpty(credentials.CLIENT_ID)) {
|
||||||
|
return cb('Coinbase is Disabled');
|
||||||
|
}
|
||||||
|
$log.debug('Init Token...');
|
||||||
|
|
||||||
|
var getToken = function(cb) {
|
||||||
|
if (accessToken) {
|
||||||
|
cb(null, accessToken);
|
||||||
|
} else {
|
||||||
|
storageService.getCoinbaseToken(credentials.NETWORK, cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getToken(function(err, accessToken) {
|
||||||
|
if (err || !accessToken) return cb();
|
||||||
|
else {
|
||||||
|
_getMainAccountId(accessToken, function(err, accountId) {
|
||||||
|
if (err) {
|
||||||
|
if (err.errors && err.errors[0] && err.errors[0].id == 'expired_token') {
|
||||||
|
$log.debug('Refresh token');
|
||||||
|
storageService.getCoinbaseRefreshToken(credentials.NETWORK, function(err, refreshToken) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
_refreshToken(refreshToken, function(err, newToken) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
return cb(null, {accessToken: newToken, accountId: accountId});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return cb(null, {accessToken: accessToken, accountId: accountId});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -124,7 +210,7 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
return cb(null, data.data);
|
return cb(null, data.data);
|
||||||
}, function(data) {
|
}, function(data) {
|
||||||
$log.error('Coinbase Get Accounts: ERROR ' + data.statusText);
|
$log.error('Coinbase Get Accounts: ERROR ' + data.statusText);
|
||||||
return cb(data.data);
|
return cb(data.data || 'Could not get the accounts');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -331,9 +417,8 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
|
|
||||||
// Pending transactions
|
// Pending transactions
|
||||||
|
|
||||||
root.savePendingTransaction = function(ctx, opts, cb) {
|
var _savePendingTransaction = function(ctx, opts, cb) {
|
||||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, oldTxs) {
|
||||||
storageService.getCoinbaseTxs(network, function(err, oldTxs) {
|
|
||||||
if (lodash.isString(oldTxs)) {
|
if (lodash.isString(oldTxs)) {
|
||||||
oldTxs = JSON.parse(oldTxs);
|
oldTxs = JSON.parse(oldTxs);
|
||||||
}
|
}
|
||||||
|
|
@ -350,23 +435,166 @@ angular.module('copayApp.services').factory('coinbaseService', function($http, $
|
||||||
}
|
}
|
||||||
tx = JSON.stringify(tx);
|
tx = JSON.stringify(tx);
|
||||||
|
|
||||||
storageService.setCoinbaseTxs(network, tx, function(err) {
|
storageService.setCoinbaseTxs(credentials.NETWORK, tx, function(err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.getPendingTransactions = function(cb) {
|
root.getPendingTransactions = function(accessToken, accountId, cb) {
|
||||||
var network = configService.getSync().coinbase.testnet ? 'testnet' : 'livenet';
|
var coinbasePendingTransactions;
|
||||||
storageService.getCoinbaseTxs(network, function(err, txs) {
|
storageService.getCoinbaseTxs(credentials.NETWORK, function(err, txs) {
|
||||||
var _txs = txs ? JSON.parse(txs) : {};
|
txs = txs ? JSON.parse(txs) : {};
|
||||||
return cb(err, _txs);
|
coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs;
|
||||||
|
lodash.forEach(txs, function(dataFromStorage, txId) {
|
||||||
|
if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
|
||||||
|
(dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
|
||||||
|
dataFromStorage.status == 'error' ||
|
||||||
|
(dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return;
|
||||||
|
root.getTransaction(accessToken, accountId, txId, function(err, tx) {
|
||||||
|
if (err) {
|
||||||
|
_savePendingTransaction(dataFromStorage, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_updateCoinbasePendingTransactions(dataFromStorage, tx.data);
|
||||||
|
coinbasePendingTransactions[txId] = dataFromStorage;
|
||||||
|
if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
|
||||||
|
root.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
|
||||||
|
if (err) {
|
||||||
|
_savePendingTransaction(dataFromStorage, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
var newSellPrice = s.data.amount;
|
||||||
|
var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100);
|
||||||
|
if (variance < dataFromStorage.price_sensitivity.value) {
|
||||||
|
_sellPending(tx.data, accessToken, accountId);
|
||||||
|
} else {
|
||||||
|
var error = {
|
||||||
|
errors: [{
|
||||||
|
message: 'Price falls over the selected percentage'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
_savePendingTransaction(dataFromStorage, {
|
||||||
|
status: 'error',
|
||||||
|
error: error
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) {
|
||||||
|
_sendToCopay(dataFromStorage, accessToken, accountId);
|
||||||
|
} else {
|
||||||
|
_savePendingTransaction(dataFromStorage, {}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return cb(null, coinbasePendingTransactions);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
root.logout = function(network, cb) {
|
var _sellPending = function(tx, accessToken, accountId) {
|
||||||
storageService.removeCoinbaseToken(network, function() {
|
if (!tx) return;
|
||||||
storageService.removeCoinbaseRefreshToken(network, function() {
|
var data = tx.amount;
|
||||||
|
data['commit'] = true;
|
||||||
|
root.sellRequest(accessToken, accountId, data, function(err, res) {
|
||||||
|
if (err) {
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!res.data.transaction) {
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
remove: true
|
||||||
|
}, function(err) {
|
||||||
|
root.getTransaction(accessToken, accountId, res.data.transaction.id, function(err, updatedTx) {
|
||||||
|
_savePendingTransaction(updatedTx.data, {}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _sendToCopay = function(tx, accessToken, accountId) {
|
||||||
|
if (!tx) return;
|
||||||
|
var data = {
|
||||||
|
to: tx.toAddr,
|
||||||
|
amount: tx.amount.amount,
|
||||||
|
currency: tx.amount.currency,
|
||||||
|
description: 'To Copay Wallet'
|
||||||
|
};
|
||||||
|
root.sendTo(accessToken, accountId, data, function(err, res) {
|
||||||
|
if (err) {
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!res.data.id) {
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
status: 'error',
|
||||||
|
error: err
|
||||||
|
}, function(err) {
|
||||||
|
if (err) $log.debug(err);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
root.getTransaction(accessToken, accountId, res.data.id, function(err, sendTx) {
|
||||||
|
_savePendingTransaction(tx, {
|
||||||
|
remove: true
|
||||||
|
}, function(err) {
|
||||||
|
_savePendingTransaction(sendTx.data, {}, function(err) {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) {
|
||||||
|
for (var i = 1; i < arguments.length; i++) {
|
||||||
|
for (var prop in arguments[i]) {
|
||||||
|
var val = arguments[i][prop];
|
||||||
|
if (typeof val == "object")
|
||||||
|
_updateCoinbasePendingTransactions(obj[prop], val);
|
||||||
|
else
|
||||||
|
obj[prop] = val ? val : obj[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
root.logout = function(cb) {
|
||||||
|
storageService.removeCoinbaseToken(credentials.NETWORK, function() {
|
||||||
|
storageService.removeCoinbaseRefreshToken(credentials.NETWORK, function() {
|
||||||
return cb();
|
return cb();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
||||||
},
|
},
|
||||||
|
|
||||||
coinbase: {
|
coinbase: {
|
||||||
enabled: false, //disable coinbase for this release
|
enabled: true,
|
||||||
testnet: false
|
testnet: false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -230,10 +230,6 @@ angular.module('copayApp.services').factory('configService', function(storageSer
|
||||||
configCache.aliasFor = configCache.aliasFor || {};
|
configCache.aliasFor = configCache.aliasFor || {};
|
||||||
configCache.emailFor = configCache.emailFor || {};
|
configCache.emailFor = configCache.emailFor || {};
|
||||||
|
|
||||||
// Coinbase
|
|
||||||
// Disabled for testnet
|
|
||||||
configCache.coinbase.testnet = false;
|
|
||||||
|
|
||||||
$log.debug('Preferences read:', configCache)
|
$log.debug('Preferences read:', configCache)
|
||||||
|
|
||||||
lodash.each(root._queue, function(x) {
|
lodash.each(root._queue, function(x) {
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,9 @@
|
||||||
<span class="toggle-label" translate>Enable Glidera Service</span>
|
<span class="toggle-label" translate>Enable Glidera Service</span>
|
||||||
</ion-toggle>
|
</ion-toggle>
|
||||||
|
|
||||||
<!-- disable coinbase for this release -->
|
<ion-toggle ng-show="!isWP" ng-model="coinbaseEnabled.value" toggle-class="toggle-balanced" ng-change="coinbaseChange()">
|
||||||
|
|
||||||
<!-- <ion-toggle ng-show="!isWP" ng-model="coinbaseEnabled" toggle-class="toggle-balanced" ng-change="coinbaseChange()">
|
|
||||||
<span class="toggle-label" translate>Enable Coinbase Service</span>
|
<span class="toggle-label" translate>Enable Coinbase Service</span>
|
||||||
</ion-toggle> -->
|
</ion-toggle>
|
||||||
|
|
||||||
|
|
||||||
<div class="item item-divider" translate>Wallet Operation</div>
|
<div class="item item-divider" translate>Wallet Operation</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,10 @@
|
||||||
</ion-nav-bar>
|
</ion-nav-bar>
|
||||||
<ion-content>
|
<ion-content>
|
||||||
<ion-list>
|
<ion-list>
|
||||||
|
<ion-item class="item item-icon-right" ui-sref="tabs.buyandsell.coinbase">
|
||||||
|
<img src="img/coinbase-logo.png" width="90">
|
||||||
|
<i class="icon bp-arrow-right"></i>
|
||||||
|
</ion-item>
|
||||||
<ion-item class="item item-icon-right" ui-sref="tabs.buyandsell.glidera">
|
<ion-item class="item item-icon-right" ui-sref="tabs.buyandsell.glidera">
|
||||||
<img src="img/glidera-logo.png" width="90">
|
<img src="img/glidera-logo.png" width="90">
|
||||||
<i class="icon bp-arrow-right"></i>
|
<i class="icon bp-arrow-right"></i>
|
||||||
|
|
|
||||||
|
|
@ -1,59 +1,30 @@
|
||||||
|
<ion-view id="coinbase">
|
||||||
<div class="topbar-container">
|
<ion-nav-bar class="bar-royal">
|
||||||
<nav ng-controller="topbarController as topbar"
|
<ion-nav-back-button>
|
||||||
class="tab-bar"
|
</ion-nav-back-button>
|
||||||
ng-style="{'background-color': '#2b71b1'}">
|
<ion-nav-title>Coinbase</ion-nav-title>
|
||||||
<section class="left-small">
|
<ion-nav-buttons side="secondary">
|
||||||
<a class="p10"
|
<button ng-show="accountId" class="button no-border" ui-sref="tabs.buyandsell.coinbase.preferences">
|
||||||
ng-click="topbar.goHome()">
|
<i class="icon ion-ios-settings"></i>
|
||||||
<span class="text-close">Close</span>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="right-small" ng-show="index.coinbaseAccount">
|
|
||||||
<a class="p10" href ui-sref="preferencesCoinbase">
|
|
||||||
<i class="fi-widget size-24"></i>
|
|
||||||
</a>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="middle tab-bar-section">
|
|
||||||
<h1 class="title ellipsis">
|
|
||||||
Buy & Sell Bitcoin
|
|
||||||
</h1>
|
|
||||||
</section>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content coinbase p20b" ng-controller="coinbaseController as coinbase">
|
|
||||||
<div class="row" ng-show="index.coinbaseError || (index.coinbaseToken && !index.coinbaseAccount)">
|
|
||||||
<div class="m20b box-notification" ng-show="index.coinbaseError">
|
|
||||||
<ul class="no-bullet m0 text-warning size-12">
|
|
||||||
<li ng-repeat="err in index.coinbaseError.errors" ng-bind-html="err.message"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="m20b box-notification" ng-show="index.coinbaseToken && !index.coinbaseAccount">
|
|
||||||
<div class="text-warning">
|
|
||||||
<span>Your primary account should be a WALLET. Set your wallet account as primary and try again.</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="m10t text-center">
|
|
||||||
<button
|
|
||||||
class="dark-gray outline round tiny"
|
|
||||||
ng-click="index.initCoinbase(index.coinbaseToken)">
|
|
||||||
Reconnect
|
|
||||||
</button>
|
</button>
|
||||||
<div class="m20t size-12">
|
</ion-nav-buttons>
|
||||||
Or go to <a class="text-gray" href ui-sref="preferencesCoinbase">Preferences</a> and log out manually.
|
</ion-nav-bar>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="!index.coinbaseToken && !index.coinbaseError" class="row">
|
<ion-content>
|
||||||
<div class="box-notification text-center size-12 text-warning" ng-show="index.coinbaseTestnet">
|
|
||||||
<i class="fi-info"></i>
|
<div class="box-notification warning m0" ng-show="network == 'testnet'">
|
||||||
Testnet wallets only work with Coinbase Sandbox Accounts
|
Testnet wallets only work with Coinbase Sandbox Accounts
|
||||||
</div>
|
</div>
|
||||||
<div class="columns" ng-init="showOauthForm = false">
|
|
||||||
|
<div class="text-center" ng-show="error">
|
||||||
|
<div class="m20b box-notification" ng-show="error">
|
||||||
|
<ul class="no-bullet m0 size-12">
|
||||||
|
<li ng-repeat="err in error.errors" ng-bind-html="err.message"></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="!accountId && !error" ng-init="showOauthForm = false">
|
||||||
<div class="text-center m20v">
|
<div class="text-center m20v">
|
||||||
<img src="img/coinbase-logo.png" width="200">
|
<img src="img/coinbase-logo.png" width="200">
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -61,111 +32,95 @@
|
||||||
|
|
||||||
<p class="m20t text-gray size-12">Connect your Coinbase account to get started</p>
|
<p class="m20t text-gray size-12">Connect your Coinbase account to get started</p>
|
||||||
|
|
||||||
<a class="button light-gray outline round small"
|
<button class="button button-standard button-primary"
|
||||||
ng-click="coinbase.openAuthenticateWindow(); showOauthForm = true">
|
ng-click="coinbase.openAuthenticateWindow(); showOauthForm = true">
|
||||||
Connect to Coinbase
|
Connect to Coinbase
|
||||||
</a>
|
</button>
|
||||||
<div>
|
<div class="m20t">
|
||||||
<a href ng-click="showOauthForm = true" class="text-gray size-12">
|
<a href ng-click="showOauthForm = true" class="text-gray size-12">
|
||||||
Do you already have the Oauth Code?
|
Do you already have the Oauth Code?
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center" ng-show="showOauthForm">
|
<div ng-show="showOauthForm">
|
||||||
<div class="text-left box-notification" ng-show="coinbase.error">
|
<div class="text-left box-notification" ng-show="coinbase.error">
|
||||||
<ul class="no-bullet m0 text-warning size-12">
|
<ul class="no-bullet m0 text-warning size-12">
|
||||||
<li ng-repeat="err in coinbase.error.errors" ng-bind-html="err.message"></li>
|
<li ng-repeat="err in coinbase.error.errors" ng-bind-html="err.message"></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<form name="oauthCodeForm" ng-submit="coinbase.submitOauthCode(code)" novalidate>
|
<form name="oauthCodeForm" ng-submit="coinbase.submitOauthCode(code)" novalidate>
|
||||||
<label>OAuth Code</label>
|
<div class="list settings-input-group">
|
||||||
<input type="text" ng-model="code" ng-disabled="coinbase.loading"
|
<label class="item item-input item-stacked-label">
|
||||||
ng-attr-placeholder="{{'Paste the authorization code here'}}" required>
|
<span class="input-label">OAuth Code</span>
|
||||||
<input
|
<input type="text"
|
||||||
class="button expand round"
|
ng-model="code"
|
||||||
ng-style="{'background-color': '#2b71b1'}"
|
ng-attr-placeholder="{{'Paste the authorization code here'}}" required>
|
||||||
type="submit" value="Get started" ng-disabled="oauthCodeForm.$invalid || coinbase.loading">
|
</label>
|
||||||
</form>
|
|
||||||
<button class="button light-gray expand outline round"
|
|
||||||
ng-click="showOauthForm = false; index.coinbaseError = null; coinbase.error = null">
|
|
||||||
<i class="fi-arrow-left"></i> <span class="tu">Back</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="index.coinbaseToken && index.coinbaseAccount && !index.coinbaseError">
|
|
||||||
|
|
||||||
<div class="p20v text-center" ng-show="index.coinbaseAccount" ng-click="index.updateCoinbase({updateAccount: true})">
|
|
||||||
<img src="img/coinbase-logo.png" width="100">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul ng-show="index.coinbaseAccount" class="no-bullet m0 size-12">
|
|
||||||
<li class="line-b line-t p15 coinbase-pointer"
|
|
||||||
href ui-sref="buyCoinbase">
|
|
||||||
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="30">
|
|
||||||
<span class="m10 text-normal text-bold">Buy Bitcoin</span>
|
|
||||||
<span class="right text-gray">
|
|
||||||
<i class="icon-arrow-right3 size-24 right"></i>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
<li class="line-b p15 coinbase-pointer"
|
|
||||||
href ui-sref="sellCoinbase">
|
|
||||||
<img src="img/sell-bitcoin.svg" alt="sell bitcoin" width="30">
|
|
||||||
<span class="m10 text-normal text-bold">Sell Bitcoin</span>
|
|
||||||
<span class="right text-gray">
|
|
||||||
<i class="icon-arrow-right3 size-24 right"></i>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div ng-show="index.coinbasePendingTransactions && !index.coinbaseError">
|
|
||||||
<h4 class="title">Activity</h4>
|
|
||||||
<div class="m20b box-notification" ng-show="index.coinbasePendingError">
|
|
||||||
<ul class="no-bullet m0 text-warning size-12">
|
|
||||||
<li ng-repeat="err in index.coinbasePendingError.errors" ng-bind-html="err.message"></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div ng-repeat="(id, tx) in index.coinbasePendingTransactions | orderObjectBy:'updated_at':true track by $index"
|
|
||||||
ng-click="coinbase.openTxModal(tx)"
|
|
||||||
class="row collapse coinbase-last-transactions-content">
|
|
||||||
<div class="large-2 medium-2 small-2 columns">
|
|
||||||
<img src="img/bought-pending.svg" alt="bought" width="24" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status != 'completed'">
|
|
||||||
<img src="img/bought.svg" alt="bought" width="30" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status == 'completed'">
|
|
||||||
<img src="img/sold-pending.svg" alt="sold" width="24" ng-show="tx.from && tx.type == 'send'">
|
|
||||||
<img src="img/sold.svg" alt="sold" width="30" ng-show="!tx.from && tx.type == 'sell' && tx.status == 'completed'">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="large-5 medium-5 small-5 columns">
|
|
||||||
<div class="size-12 m5t">
|
|
||||||
<span ng-show="tx.type == 'sell' && tx.status == 'completed'">Sold</span>
|
|
||||||
<span ng-show="tx.type == 'buy' && tx.status == 'completed'">Bought</span>
|
|
||||||
<span class="text-bold">
|
|
||||||
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
|
||||||
{{tx.amount.currency}}
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<input
|
||||||
<div class="large-4 medium-4 small-4 columns text-right">
|
class="button button-standard button-primary"
|
||||||
<div ng-show="tx.error" class="m5t size-12 text-warning">
|
type="submit" value="Connect Coinbase Account" ng-disabled="oauthCodeForm.$invalid">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="accountId && !error">
|
||||||
|
|
||||||
|
<div class="list card"
|
||||||
|
ng-show="accountId">
|
||||||
|
<a class="item item-icon-right"
|
||||||
|
href ui-sref="tabs.buyandsell.coinbase.buy">
|
||||||
|
<img src="img/buy-bitcoin.svg" alt="buy bitcoin" width="35" class="item-img-buy">
|
||||||
|
Buy Bitcoin
|
||||||
|
<i class="icon bp-arrow-right"></i>
|
||||||
|
</a>
|
||||||
|
<a class="item item-icon-right"
|
||||||
|
href ui-sref="tabs.buyandsell.coinbase.sell">
|
||||||
|
<img src="img/sell-bitcoin.svg" alt="buy bitcoin" width="35" class="item-img-sell">
|
||||||
|
Sell Bitcoin
|
||||||
|
<i class="icon bp-arrow-right"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="list card">
|
||||||
|
<div class="item item-heading" ng-click="updateTransactions()">
|
||||||
|
Activity
|
||||||
|
</div>
|
||||||
|
<a class="item"
|
||||||
|
ng-if="pendingTransactions && !error"
|
||||||
|
ng-repeat="(id, tx) in pendingTransactions | orderObjectBy:'updated_at':true track by $index"
|
||||||
|
ng-click="coinbase.openTxModal(tx)">
|
||||||
|
|
||||||
|
<span class="item-note">
|
||||||
|
<div ng-show="tx.error">
|
||||||
Error
|
Error
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="!tx.error" class="m5t size-12 text-gray">
|
<div ng-show="!tx.error">
|
||||||
<div ng-show="tx.status == 'completed'">
|
<div ng-show="tx.status == 'completed'">
|
||||||
<time ng-if="tx.created_at">{{tx.created_at | amTimeAgo}}</time>
|
<time ng-if="tx.created_at">{{tx.created_at | amTimeAgo}}</time>
|
||||||
</div>
|
</div>
|
||||||
<div ng-show="tx.status == 'pending'">
|
<div ng-show="tx.status == 'pending'">
|
||||||
<span class="label outline gray radius text-gray text-info" ng-if="tx.status == 'pending'">Pending</span>
|
<span ng-if="tx.status == 'pending'">Pending</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</span>
|
||||||
<div class="large-1 medium-1 small-1 columns text-right">
|
|
||||||
<i class="icon-arrow-right3 size-18"></i>
|
<img class="left m10r" src="img/bought-pending.svg" alt="bought" width="24" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status != 'completed'">
|
||||||
</div>
|
<img class="left m10r" src="img/bought.svg" alt="bought" width="30" ng-show="(tx.type == 'buy' || (tx.to && tx.type == 'send')) && tx.status == 'completed'">
|
||||||
</div>
|
<img class="left m10r" src="img/sold-pending.svg" alt="sold" width="24" ng-show="tx.from && tx.type == 'send'">
|
||||||
|
<img class="left m10r" src="img/sold.svg" alt="sold" width="30" ng-show="!tx.from && tx.type == 'sell' && tx.status == 'completed'">
|
||||||
|
|
||||||
|
<h2>
|
||||||
|
<span ng-show="tx.type == 'sell' && tx.status == 'completed'">Sold</span>
|
||||||
|
<span ng-show="tx.type == 'buy' && tx.status == 'completed'">Bought</span>
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
<span ng-if="tx.type == 'sell' || (tx.type == 'send' && tx.from)">-</span>{{tx.amount.amount.replace('-','')}}
|
||||||
|
{{tx.amount.currency}}
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</ion-content>
|
||||||
<div class="extra-margin-bottom"></div>
|
</ion-view>
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -124,10 +124,10 @@
|
||||||
<img src="img/glidera-logo.png" width="90"/>
|
<img src="img/glidera-logo.png" width="90"/>
|
||||||
<i class="icon bp-arrow-right"></i>
|
<i class="icon bp-arrow-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<!-- disable coinbase for this release -->
|
<a ng-if="coinbaseEnabled" ui-sref="tabs.buyandsell.coinbase" class="item item-extra-padding item-sub item-icon-right">
|
||||||
<!-- <a ng-if="coinbaseEnabled" ui-sref="exchange.coinbase" class="item">
|
<img src="img/coinbase-logo.png" width="90">
|
||||||
<img src="img/coinbase-logo.png" width="90"> TODO
|
<i class="icon bp-arrow-right"></i>
|
||||||
</a> -->
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@
|
||||||
<span translate>Add BitPay Visa® Card</span>
|
<span translate>Add BitPay Visa® Card</span>
|
||||||
<i class="icon bp-arrow-right"></i>
|
<i class="icon bp-arrow-right"></i>
|
||||||
</a>
|
</a>
|
||||||
<a ng-if="!externalServices.BuyAndSell && (coinbaseEnabled || glideraEnabled)" ui-sref="tabs.buyandsell.glidera" class="item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
<a ng-if="!externalServices.BuyAndSell && (coinbaseEnabled || glideraEnabled)" ui-sref="tabs.buyandsell" class="item item-sub item-icon-left item-big-icon-left item-icon-right next-step">
|
||||||
<i class="icon big-icon-svg">
|
<i class="icon big-icon-svg">
|
||||||
<div class="bg icon-buy-bitcoin"></div>
|
<div class="bg icon-buy-bitcoin"></div>
|
||||||
</i>
|
</i>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue