mirror of https://github.com/BTCPrivate/copay.git
Merge branch 'master' into bugs/ui
This commit is contained in:
commit
2d33dcfef6
|
@ -83,7 +83,6 @@ module.exports = function(grunt) {
|
|||
'js/shell.js', // shell must be loaded before moment due to the way moment loads in a commonjs env
|
||||
'lib/moment/min/moment.min.js',
|
||||
'lib/qrcode-generator/js/qrcode.js',
|
||||
'lib/peer.js',
|
||||
'lib/bitcore.js',
|
||||
'lib/crypto-js/rollups/sha256.js',
|
||||
'lib/crypto-js/rollups/pbkdf2.js',
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"angular-foundation": "*",
|
||||
"angular-route": "~1.2.14",
|
||||
"angular-qrcode": "~3.1.0",
|
||||
"peerjs": "=0.3.8",
|
||||
"angular-mocks": "~1.2.14",
|
||||
"mocha": "~1.18.2",
|
||||
"chai": "~1.9.1",
|
||||
|
|
|
@ -3,10 +3,13 @@ var defaultConfig = {
|
|||
// DEFAULT network (livenet or testnet)
|
||||
networkName: 'testnet',
|
||||
forceNetwork: false,
|
||||
logLevel: 'info',
|
||||
|
||||
// DEFAULT unit: Bit
|
||||
unitName: 'bits',
|
||||
unitToSatoshi: 100,
|
||||
alternativeName: 'US Dollar',
|
||||
alternativeIsoCode: 'USD',
|
||||
|
||||
// wallet limits
|
||||
limits: {
|
||||
|
@ -54,7 +57,11 @@ var defaultConfig = {
|
|||
storageSalt: 'mjuBtGybi/4=',
|
||||
},
|
||||
|
||||
disableVideo: true,
|
||||
rate: {
|
||||
url: 'https://bitpay.com/api/rates',
|
||||
updateFrequencySeconds: 60 * 60
|
||||
},
|
||||
|
||||
verbose: 1,
|
||||
};
|
||||
if (typeof module !== 'undefined')
|
||||
|
|
|
@ -205,23 +205,6 @@ a:hover {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.sidebar ul.copayer-list {
|
||||
list-style-type: none;
|
||||
padding:0; margin:0;
|
||||
}
|
||||
|
||||
.sidebar ul.copayer-list li {
|
||||
margin-top: 15px;
|
||||
font-weight: 100;
|
||||
font-size: 12px;
|
||||
color: #C9C9C9;
|
||||
}
|
||||
|
||||
.sidebar ul.copayer-list img {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.button.small.side-bar {
|
||||
padding: 0rem 0.4rem;
|
||||
}
|
||||
|
@ -954,7 +937,15 @@ button, .button, p {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.video-box {
|
||||
.copay-box-small {
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
padding-bottom: 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.copay-box {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
margin-right: 20px;
|
||||
|
@ -962,11 +953,6 @@ button, .button, p {
|
|||
float: left;
|
||||
}
|
||||
|
||||
.video-small {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.icon-input {
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
|
|
|
@ -18,11 +18,6 @@ if (localConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
var log = function() {
|
||||
if (config.verbose) console.log(arguments);
|
||||
}
|
||||
|
||||
|
||||
var copayApp = window.copayApp = angular.module('copayApp', [
|
||||
'ngRoute',
|
||||
'angularMoment',
|
||||
|
|
|
@ -26,4 +26,15 @@ angular.module('copayApp.controllers').controller('CopayersController',
|
|||
});
|
||||
};
|
||||
|
||||
// Cached list of copayers
|
||||
$scope.copayers = $rootScope.wallet.getRegisteredPeerIds();
|
||||
|
||||
$scope.copayersList = function() {
|
||||
return $rootScope.wallet.getRegisteredPeerIds();
|
||||
}
|
||||
|
||||
$scope.isBackupReady = function(copayer) {
|
||||
return $rootScope.wallet.publicKeyRing.isBackupReady(copayer.copayerId);
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -32,12 +32,11 @@ var valid_pairs = {
|
|||
'1,12': 489
|
||||
};
|
||||
|
||||
angular.module('copayApp.controllers').controller('SetupController',
|
||||
angular.module('copayApp.controllers').controller('CreateController',
|
||||
function($scope, $rootScope, $location, $timeout, walletFactory, controllerUtils, Passphrase, backupService, notification) {
|
||||
controllerUtils.redirIfLogged();
|
||||
|
||||
$rootScope.fromSetup = true;
|
||||
$rootScope.videoInfo = {};
|
||||
$scope.loading = false;
|
||||
$scope.walletPassword = $rootScope.walletPassword;
|
||||
$scope.isMobile = !!window.cordova;
|
|
@ -2,14 +2,67 @@
|
|||
var bitcore = require('bitcore');
|
||||
|
||||
angular.module('copayApp.controllers').controller('SendController',
|
||||
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils) {
|
||||
function($scope, $rootScope, $window, $timeout, $anchorScroll, $modal, isMobile, notification, controllerUtils, rateService) {
|
||||
$scope.title = 'Send';
|
||||
$scope.loading = false;
|
||||
var satToUnit = 1 / config.unitToSatoshi;
|
||||
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
||||
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
||||
$scope.unitToSatoshi = config.unitToSatoshi;
|
||||
$scope.minAmount = config.limits.minAmountSatoshi * satToUnit;
|
||||
|
||||
$scope.alternativeName = config.alternativeName;
|
||||
$scope.alternativeIsoCode = config.alternativeIsoCode;
|
||||
|
||||
$scope.isRateAvailable = false;
|
||||
$scope.rateService = rateService;
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
$scope.isRateAvailable = true;
|
||||
$scope.$digest();
|
||||
});
|
||||
|
||||
/**
|
||||
* Setting the two related amounts as properties prevents an infinite
|
||||
* recursion for watches while preserving the original angular updates
|
||||
*/
|
||||
Object.defineProperty($scope,
|
||||
"alternative", {
|
||||
get: function () {
|
||||
return this._alternative;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this._alternative = newValue;
|
||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||
this._amount = Number.parseFloat(
|
||||
(rateService.fromFiat(newValue, config.alternativeIsoCode) * satToUnit
|
||||
).toFixed(config.unitDecimals), 10);
|
||||
} else {
|
||||
this._amount = 0;
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty($scope,
|
||||
"amount", {
|
||||
get: function () {
|
||||
return this._amount;
|
||||
},
|
||||
set: function (newValue) {
|
||||
this._amount = newValue;
|
||||
if (typeof(newValue) === 'number' && $scope.isRateAvailable) {
|
||||
this._alternative = Number.parseFloat(
|
||||
(rateService.toFiat(newValue * config.unitToSatoshi, config.alternativeIsoCode)
|
||||
).toFixed(2), 10);
|
||||
} else {
|
||||
this._alternative = 0;
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
$scope.loadTxs = function() {
|
||||
var opts = {
|
||||
pending: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils) {
|
||||
angular.module('copayApp.controllers').controller('SettingsController', function($scope, $rootScope, $window, $location, controllerUtils, rateService) {
|
||||
|
||||
controllerUtils.redirIfLogged();
|
||||
$scope.title = 'Settings';
|
||||
|
@ -8,27 +8,46 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
|||
$scope.insightHost = config.blockchain.host;
|
||||
$scope.insightPort = config.blockchain.port;
|
||||
$scope.insightSecure = config.blockchain.schema === 'https';
|
||||
$scope.disableVideo = typeof config.disableVideo === undefined ? true : config.disableVideo;
|
||||
$scope.forceNetwork = config.forceNetwork;
|
||||
|
||||
$scope.unitOpts = [{
|
||||
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||
shortName: 'SAT',
|
||||
value: 1
|
||||
value: 1,
|
||||
decimals: 0
|
||||
}, {
|
||||
name: 'bits (1,000,000 bits = 1BTC)',
|
||||
shortName: 'bits',
|
||||
value: 100
|
||||
value: 100,
|
||||
decimals: 2
|
||||
}, {
|
||||
name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||
shortName: 'mBTC',
|
||||
value: 100000
|
||||
value: 100000,
|
||||
decimals: 5
|
||||
}, {
|
||||
name: 'BTC',
|
||||
shortName: 'BTC',
|
||||
value: 100000000
|
||||
value: 100000000,
|
||||
decimals: 8
|
||||
}];
|
||||
|
||||
$scope.selectedAlternative = {
|
||||
name: config.alternativeName,
|
||||
isoCode: config.alternativeIsoCode
|
||||
};
|
||||
$scope.alternativeOpts = rateService.isAvailable ?
|
||||
rateService.listAlternatives() : [$scope.selectedAlternative];
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
$scope.alternativeOpts = rateService.listAlternatives();
|
||||
for (var ii in $scope.alternativeOpts) {
|
||||
if (config.alternativeIsoCode === $scope.alternativeOpts[ii].isoCode) {
|
||||
$scope.selectedAlternative = $scope.alternativeOpts[ii];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (var ii in $scope.unitOpts) {
|
||||
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
||||
$scope.selectedUnit = $scope.unitOpts[ii];
|
||||
|
@ -65,10 +84,13 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
|||
schema: $scope.insightSecure ? 'https' : 'http',
|
||||
},
|
||||
network: network,
|
||||
disableVideo: $scope.disableVideo,
|
||||
unitName: $scope.selectedUnit.shortName,
|
||||
unitToSatoshi: $scope.selectedUnit.value,
|
||||
version: copay.version,
|
||||
unitDecimals: $scope.selectedUnit.decimals,
|
||||
alternativeName: $scope.selectedAlternative.name,
|
||||
alternativeIsoCode: $scope.selectedAlternative.isoCode,
|
||||
|
||||
version: copay.version
|
||||
}));
|
||||
|
||||
// Go home reloading the application
|
||||
|
|
|
@ -4,7 +4,7 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
|
|||
|
||||
$scope.menu = [{
|
||||
'title': 'Receive',
|
||||
'icon': 'fi-arrow-left',
|
||||
'icon': 'fi-download',
|
||||
'link': 'receive'
|
||||
}, {
|
||||
'title': 'Send',
|
||||
|
@ -15,8 +15,8 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
|
|||
'icon': 'fi-clipboard-pencil',
|
||||
'link': 'history'
|
||||
}, {
|
||||
'title': 'More',
|
||||
'icon': 'fi-download',
|
||||
'title': 'Settings',
|
||||
'icon': 'fi-widget',
|
||||
'link': 'more'
|
||||
}];
|
||||
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('VideoController',
|
||||
function($scope, $rootScope, $sce) {
|
||||
|
||||
$rootScope.videoInfo = {};
|
||||
|
||||
// Cached list of copayers
|
||||
$scope.copayers = $rootScope.wallet.getRegisteredPeerIds();
|
||||
|
||||
$scope.copayersList = function() {
|
||||
return $rootScope.wallet.getRegisteredPeerIds();
|
||||
}
|
||||
|
||||
$scope.hasVideo = function(copayer) {
|
||||
return $rootScope.videoInfo[copayer.peerId];
|
||||
}
|
||||
|
||||
$scope.isConnected = function(copayer) {
|
||||
return $rootScope.wallet.getOnlinePeerIDs().indexOf(copayer.peerId) != -1;
|
||||
}
|
||||
|
||||
$scope.isBackupReady = function(copayer) {
|
||||
return $rootScope.wallet.publicKeyRing.isBackupReady(copayer.copayerId);
|
||||
}
|
||||
|
||||
$scope.getVideoURL = function(copayer) {
|
||||
if (config.disableVideo) return;
|
||||
|
||||
var vi = $scope.videoInfo[copayer.peerId];
|
||||
if (!vi) return;
|
||||
|
||||
if ($scope.isConnected(copayer)) {
|
||||
// peer disconnected, remove his video
|
||||
delete $rootScope.videoInfo[copayer.peerId];
|
||||
return;
|
||||
}
|
||||
|
||||
var encoded = vi.url;
|
||||
var url = decodeURI(encoded);
|
||||
var trusted = $sce.trustAsResourceUrl(url);
|
||||
return trusted;
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -45,9 +45,10 @@ angular.module('copayApp.directives')
|
|||
link: function(scope, element, attrs, ctrl) {
|
||||
var val = function(value) {
|
||||
var availableBalanceNum = Number(($rootScope.availableBalance * config.unitToSatoshi).toFixed(0));
|
||||
var vNum = Number((value * config.unitToSatoshi).toFixed(0)) + feeSat;
|
||||
var vNum = Number((value * config.unitToSatoshi).toFixed(0));
|
||||
|
||||
if (typeof vNum == "number" && vNum > 0) {
|
||||
vNum = vNum + feeSat;
|
||||
if (availableBalanceNum < vNum || isNaN(availableBalanceNum)) {
|
||||
ctrl.$setValidity('enoughAmount', false);
|
||||
scope.notEnoughAmount = true;
|
||||
|
@ -108,20 +109,6 @@ angular.module('copayApp.directives')
|
|||
}
|
||||
}
|
||||
})
|
||||
.directive('avatar', function($rootScope, controllerUtils) {
|
||||
return {
|
||||
link: function(scope, element, attrs) {
|
||||
var peer = JSON.parse(attrs.peer)
|
||||
var peerId = peer.peerId;
|
||||
var nick = peer.nick;
|
||||
element.addClass('video-small');
|
||||
var muted = controllerUtils.getVideoMutedStatus(peerId);
|
||||
if (true || muted) { // mute everyone for now
|
||||
element.attr("muted", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.directive('contact', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
var config = require('../config');
|
||||
|
||||
var Logger = function(name) {
|
||||
this.name = name || 'log';
|
||||
this.level = 2;
|
||||
};
|
||||
|
||||
var levels = {
|
||||
'debug': 0,
|
||||
'info': 1,
|
||||
'log': 2,
|
||||
'warn': 3,
|
||||
'error': 4,
|
||||
'fatal': 5
|
||||
};
|
||||
|
||||
Object.keys(levels).forEach(function(level) {
|
||||
Logger.prototype[level] = function() {
|
||||
if (levels[level] >= levels[this.level]) {
|
||||
var str = '[' + level + '] ' + this.name + ': ' + arguments[0],
|
||||
extraArgs,
|
||||
extraArgs = [].slice.call(arguments, 1);
|
||||
if (console[level]) {
|
||||
extraArgs.unshift(str);
|
||||
console[level].apply(console, extraArgs);
|
||||
} else {
|
||||
if (extraArgs.length) {
|
||||
str += JSON.stringify(extraArgs);
|
||||
}
|
||||
console.log(str);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Logger.prototype.setLevel = function(level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
var logger = new Logger('copay');
|
||||
logger.setLevel(config.logLevel);
|
||||
|
||||
module.exports = logger;
|
|
@ -15,14 +15,12 @@ Passphrase.prototype.get = function(password) {
|
|||
keySize: 512 / 32,
|
||||
iterations: this.iterations
|
||||
});
|
||||
|
||||
return key512;
|
||||
};
|
||||
|
||||
Passphrase.prototype.getBase64 = function(password) {
|
||||
var key512 = this.get(password);
|
||||
var keyBase64 = key512.toString(CryptoJS.enc.Base64);
|
||||
|
||||
return keyBase64;
|
||||
};
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ var Base58Check = bitcore.Base58.base58Check;
|
|||
var Address = bitcore.Address;
|
||||
var PayPro = bitcore.PayPro;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var log = require('../../log');
|
||||
|
||||
var HDParams = require('./HDParams');
|
||||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
|
@ -72,12 +73,6 @@ Wallet.builderOpts = {
|
|||
feeSat: null,
|
||||
};
|
||||
|
||||
Wallet.prototype.log = function() {
|
||||
if (!this.verbose) return;
|
||||
if (console)
|
||||
console.log.apply(console, arguments);
|
||||
};
|
||||
|
||||
Wallet.getRandomId = function() {
|
||||
var r = bitcore.SecureRandom.getPseudoRandomBuffer(8).toString('hex');
|
||||
return r;
|
||||
|
@ -88,7 +83,7 @@ Wallet.prototype.seedCopayer = function(pubKey) {
|
|||
};
|
||||
|
||||
Wallet.prototype._onIndexes = function(senderId, data) {
|
||||
this.log('RECV INDEXES:', data);
|
||||
log.debug('RECV INDEXES:', data);
|
||||
var inIndexes = HDParams.fromList(data.indexes);
|
||||
var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes);
|
||||
if (hasChanged) {
|
||||
|
@ -98,7 +93,7 @@ Wallet.prototype._onIndexes = function(senderId, data) {
|
|||
};
|
||||
|
||||
Wallet.prototype._onPublicKeyRing = function(senderId, data) {
|
||||
this.log('RECV PUBLICKEYRING:', data);
|
||||
log.debug('RECV PUBLICKEYRING:', data);
|
||||
|
||||
var inPKR = PublicKeyRing.fromObj(data.publicKeyRing);
|
||||
var wasIncomplete = !this.publicKeyRing.isComplete();
|
||||
|
@ -107,7 +102,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) {
|
|||
try {
|
||||
hasChanged = this.publicKeyRing.merge(inPKR, true);
|
||||
} catch (e) {
|
||||
this.log('## WALLET ERROR', e);
|
||||
log.debug('## WALLET ERROR', e);
|
||||
this.emit('connectionError', e.message);
|
||||
return;
|
||||
}
|
||||
|
@ -205,7 +200,7 @@ Wallet.prototype._checkSentTx = function(ntxid, cb) {
|
|||
|
||||
Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||
var self = this;
|
||||
this.log('RECV TXPROPOSAL: ', data);
|
||||
log.debug('RECV TXPROPOSAL: ', data);
|
||||
var m;
|
||||
|
||||
try {
|
||||
|
@ -214,7 +209,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
|||
ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
|
||||
|
||||
} catch (e) {
|
||||
this.log('Corrupt TX proposal received from:', senderId, e);
|
||||
log.debug('Corrupt TX proposal received from:', senderId, e);
|
||||
}
|
||||
|
||||
if (m) {
|
||||
|
@ -242,7 +237,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
|||
|
||||
Wallet.prototype._onReject = function(senderId, data) {
|
||||
preconditions.checkState(data.ntxid);
|
||||
this.log('RECV REJECT:', data);
|
||||
log.debug('RECV REJECT:', data);
|
||||
|
||||
var txp = this.txProposals.get(data.ntxid);
|
||||
|
||||
|
@ -265,7 +260,7 @@ Wallet.prototype._onReject = function(senderId, data) {
|
|||
|
||||
Wallet.prototype._onSeen = function(senderId, data) {
|
||||
preconditions.checkState(data.ntxid);
|
||||
this.log('RECV SEEN:', data);
|
||||
log.debug('RECV SEEN:', data);
|
||||
|
||||
var txp = this.txProposals.get(data.ntxid);
|
||||
txp.setSeen(senderId);
|
||||
|
@ -283,7 +278,7 @@ Wallet.prototype._onSeen = function(senderId, data) {
|
|||
|
||||
Wallet.prototype._onAddressBook = function(senderId, data) {
|
||||
preconditions.checkState(data.addressBook);
|
||||
this.log('RECV ADDRESSBOOK:', data);
|
||||
log.debug('RECV ADDRESSBOOK:', data);
|
||||
var rcv = data.addressBook;
|
||||
var hasChange;
|
||||
for (var key in rcv) {
|
||||
|
@ -311,7 +306,7 @@ Wallet.prototype.updateTimestamp = function(ts) {
|
|||
|
||||
|
||||
Wallet.prototype._onNoMessages = function() {
|
||||
console.log('No messages at the server. Requesting sync'); //TODO
|
||||
log.debug('No messages at the server. Requesting sync'); //TODO
|
||||
this.sendWalletReady();
|
||||
};
|
||||
|
||||
|
@ -322,7 +317,7 @@ Wallet.prototype._onData = function(senderId, data, ts) {
|
|||
preconditions.checkArgument(ts);
|
||||
preconditions.checkArgument(typeof ts === 'number');
|
||||
|
||||
console.log('RECV', senderId, data);
|
||||
log.debug('RECV', senderId, data);
|
||||
|
||||
if (data.type !== 'walletId' && this.id !== data.walletId) {
|
||||
this.emit('corrupt', senderId);
|
||||
|
@ -375,7 +370,7 @@ Wallet.prototype._onData = function(senderId, data, ts) {
|
|||
|
||||
Wallet.prototype._onConnect = function(newCopayerId) {
|
||||
if (newCopayerId) {
|
||||
this.log('#### Setting new COPAYER:', newCopayerId);
|
||||
log.debug('#### Setting new COPAYER:', newCopayerId);
|
||||
this.sendWalletId(newCopayerId);
|
||||
}
|
||||
var peerID = this.network.peerFromCopayer(newCopayerId)
|
||||
|
@ -460,28 +455,12 @@ Wallet.prototype.netStart = function(callback) {
|
|||
self.emit('ready', net.getPeer());
|
||||
setTimeout(function() {
|
||||
self.emit('publicKeyRingUpdated', true);
|
||||
//self.scheduleConnect();
|
||||
// no connection logic for now
|
||||
self.emit('txProposalsUpdated');
|
||||
}, 10);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// not being used now
|
||||
Wallet.prototype.scheduleConnect = function() {
|
||||
var self = this;
|
||||
if (self.network.isOnline()) {
|
||||
self.connectToAll();
|
||||
self.currentDelay = self.currentDelay * 2 || self.reconnectDelay;
|
||||
setTimeout(self.scheduleConnect.bind(self), self.currentDelay);
|
||||
}
|
||||
}
|
||||
|
||||
Wallet.prototype.getOnlinePeerIDs = function() {
|
||||
return this.network.getOnlinePeerIDs();
|
||||
};
|
||||
|
||||
Wallet.prototype.getRegisteredCopayerIds = function() {
|
||||
var l = this.publicKeyRing.registeredCopayers();
|
||||
var copayers = [];
|
||||
|
@ -515,7 +494,7 @@ Wallet.prototype.keepAlive = function() {
|
|||
try {
|
||||
this.lock.keepAlive();
|
||||
} catch (e) {
|
||||
this.log(e);
|
||||
log.debug(e);
|
||||
this.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.');
|
||||
}
|
||||
};
|
||||
|
@ -525,7 +504,7 @@ Wallet.prototype.store = function() {
|
|||
|
||||
var wallet = this.toObj();
|
||||
this.storage.setFromObj(this.id, wallet);
|
||||
this.log('Wallet stored');
|
||||
log.debug('Wallet stored');
|
||||
};
|
||||
|
||||
Wallet.prototype.toObj = function() {
|
||||
|
@ -613,7 +592,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients) {
|
|||
Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
||||
preconditions.checkArgument(ntxid);
|
||||
|
||||
this.log('### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals);
|
||||
log.debug('### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals);
|
||||
this.send(recipients, {
|
||||
type: 'txProposal',
|
||||
txProposal: this.txProposals.get(ntxid).toObjTrim(),
|
||||
|
@ -623,7 +602,7 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
|||
|
||||
Wallet.prototype.sendSeen = function(ntxid) {
|
||||
preconditions.checkArgument(ntxid);
|
||||
this.log('### SENDING seen: ' + ntxid + ' TO: All');
|
||||
log.debug('### SENDING seen: ' + ntxid + ' TO: All');
|
||||
this.send(null, {
|
||||
type: 'seen',
|
||||
ntxid: ntxid,
|
||||
|
@ -633,7 +612,7 @@ Wallet.prototype.sendSeen = function(ntxid) {
|
|||
|
||||
Wallet.prototype.sendReject = function(ntxid) {
|
||||
preconditions.checkArgument(ntxid);
|
||||
this.log('### SENDING reject: ' + ntxid + ' TO: All');
|
||||
log.debug('### SENDING reject: ' + ntxid + ' TO: All');
|
||||
this.send(null, {
|
||||
type: 'reject',
|
||||
ntxid: ntxid,
|
||||
|
@ -643,7 +622,7 @@ Wallet.prototype.sendReject = function(ntxid) {
|
|||
|
||||
|
||||
Wallet.prototype.sendWalletReady = function(recipients) {
|
||||
this.log('### SENDING WalletReady TO:', recipients || 'All');
|
||||
log.debug('### SENDING WalletReady TO:', recipients || 'All');
|
||||
|
||||
this.send(recipients, {
|
||||
type: 'walletReady',
|
||||
|
@ -652,7 +631,7 @@ Wallet.prototype.sendWalletReady = function(recipients) {
|
|||
};
|
||||
|
||||
Wallet.prototype.sendWalletId = function(recipients) {
|
||||
this.log('### SENDING walletId TO:', recipients || 'All', this.id);
|
||||
log.debug('### SENDING walletId TO:', recipients || 'All', this.id);
|
||||
|
||||
this.send(recipients, {
|
||||
type: 'walletId',
|
||||
|
@ -664,7 +643,7 @@ Wallet.prototype.sendWalletId = function(recipients) {
|
|||
|
||||
|
||||
Wallet.prototype.sendPublicKeyRing = function(recipients) {
|
||||
this.log('### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj());
|
||||
log.debug('### SENDING publicKeyRing TO:', recipients || 'All', this.publicKeyRing.toObj());
|
||||
var publicKeyRing = this.publicKeyRing.toObj();
|
||||
|
||||
this.send(recipients, {
|
||||
|
@ -675,7 +654,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
|
|||
};
|
||||
Wallet.prototype.sendIndexes = function(recipients) {
|
||||
var indexes = HDParams.serialize(this.publicKeyRing.indexes);
|
||||
this.log('### INDEXES TO:', recipients || 'All', indexes);
|
||||
log.debug('### INDEXES TO:', recipients || 'All', indexes);
|
||||
|
||||
this.send(recipients, {
|
||||
type: 'indexes',
|
||||
|
@ -685,7 +664,7 @@ Wallet.prototype.sendIndexes = function(recipients) {
|
|||
};
|
||||
|
||||
Wallet.prototype.sendAddressBook = function(recipients) {
|
||||
this.log('### SENDING addressBook TO:', recipients || 'All', this.addressBook);
|
||||
log.debug('### SENDING addressBook TO:', recipients || 'All', this.addressBook);
|
||||
this.send(recipients, {
|
||||
type: 'addressbook',
|
||||
addressBook: this.addressBook,
|
||||
|
@ -796,23 +775,23 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
|||
var tx = txp.builder.build();
|
||||
if (!tx.isComplete())
|
||||
throw new Error('Tx is not complete. Can not broadcast');
|
||||
this.log('Broadcasting Transaction');
|
||||
log.debug('Broadcasting Transaction');
|
||||
var scriptSig = tx.ins[0].getScript();
|
||||
var size = scriptSig.serialize().length;
|
||||
|
||||
var txHex = tx.serialize().toString('hex');
|
||||
this.log('Raw transaction: ', txHex);
|
||||
log.debug('Raw transaction: ', txHex);
|
||||
|
||||
var self = this;
|
||||
this.blockchain.broadcast(txHex, function(err, txid) {
|
||||
self.log('BITCOIND txid:', txid);
|
||||
log.debug('BITCOIND txid:', txid);
|
||||
if (txid) {
|
||||
self.txProposals.get(ntxid).setSent(txid);
|
||||
self.sendTxProposal(ntxid);
|
||||
self.store();
|
||||
return cb(txid);
|
||||
} else {
|
||||
self.log('Sent failed. Checking if the TX was sent already');
|
||||
log.debug('Sent failed. Checking if the TX was sent already');
|
||||
self._checkSentTx(ntxid, function(txid) {
|
||||
if (txid)
|
||||
self.store();
|
||||
|
@ -1003,10 +982,8 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
|
|||
self.emit('txProposalsUpdated');
|
||||
}
|
||||
|
||||
self.log('You are currently on this BTC network:');
|
||||
self.log(network);
|
||||
self.log('The server sent you a message:');
|
||||
self.log(memo);
|
||||
log.debug('You are currently on this BTC network:', network);
|
||||
log.debug('The server sent you a message:', memo);
|
||||
|
||||
return cb(ntxid, merchantData);
|
||||
});
|
||||
|
@ -1025,7 +1002,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
|||
|
||||
var tx = txp.builder.build();
|
||||
if (!tx.isComplete()) return;
|
||||
this.log('Sending Transaction');
|
||||
log.debug('Sending Transaction');
|
||||
|
||||
var refund_outputs = [];
|
||||
|
||||
|
@ -1085,8 +1062,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
|||
|
||||
pay = pay.serialize();
|
||||
|
||||
this.log('Sending Payment Message:');
|
||||
this.log(pay.toString('hex'));
|
||||
log.debug('Sending Payment Message:', pay.toString('hex'));
|
||||
|
||||
var buf = new ArrayBuffer(pay.length);
|
||||
var view = new Uint8Array(buf);
|
||||
|
@ -1127,8 +1103,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
|||
var payment = ack.get('payment');
|
||||
var memo = ack.get('memo');
|
||||
|
||||
this.log('Our payment was acknowledged!');
|
||||
this.log('Message from Merchant: %s', memo);
|
||||
log.debug('Our payment was acknowledged!');
|
||||
log.debug('Message from Merchant: %s', memo);
|
||||
|
||||
payment = PayPro.Payment.decode(payment);
|
||||
var pay = new PayPro();
|
||||
|
@ -1141,7 +1117,7 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
|||
var tx = payment.message.transactions[0];
|
||||
|
||||
if (!tx) {
|
||||
this.log('Sending to server was not met with a returned tx.');
|
||||
log.debug('Sending to server was not met with a returned tx.');
|
||||
return this._checkSentTx(ntxid, function(txid) {
|
||||
self.log('[Wallet.js.1048:txid:%s]', txid);
|
||||
if (txid) self.store();
|
||||
|
@ -1159,8 +1135,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
|||
|
||||
var txid = tx.getHash().toString('hex');
|
||||
var txHex = tx.serialize().toString('hex');
|
||||
this.log('Raw transaction: ', txHex);
|
||||
this.log('BITCOIND txid:', txid);
|
||||
log.debug('Raw transaction: ', txHex);
|
||||
log.debug('BITCOIND txid:', txid);
|
||||
this.txProposals.get(ntxid).setSent(txid);
|
||||
this.sendTxProposal(ntxid);
|
||||
this.store();
|
||||
|
@ -1260,10 +1236,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
|
|||
|
||||
if (options.fetch) return;
|
||||
|
||||
this.log('');
|
||||
this.log('Created transaction:');
|
||||
this.log(b.tx.getStandardizedObject());
|
||||
this.log('');
|
||||
log.debug('Created transaction: %s', b.tx.getStandardizedObject());
|
||||
|
||||
var myId = this.getMyCopayerId();
|
||||
var now = Date.now();
|
||||
|
@ -1661,7 +1634,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
|||
|
||||
Wallet.prototype.updateIndexes = function(callback) {
|
||||
var self = this;
|
||||
self.log('Updating indexes...');
|
||||
log.debug('Updating indexes...');
|
||||
|
||||
var tasks = this.publicKeyRing.indexes.map(function(index) {
|
||||
return function(callback) {
|
||||
|
@ -1671,7 +1644,7 @@ Wallet.prototype.updateIndexes = function(callback) {
|
|||
|
||||
async.parallel(tasks, function(err) {
|
||||
if (err) callback(err);
|
||||
self.log('Indexes updated');
|
||||
log.debug('Indexes updated');
|
||||
self.emit('publicKeyRingUpdated');
|
||||
self.store();
|
||||
callback();
|
||||
|
@ -1747,7 +1720,7 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb)
|
|||
|
||||
|
||||
Wallet.prototype.close = function() {
|
||||
this.log('## CLOSING');
|
||||
log.debug('## CLOSING');
|
||||
this.lock.release();
|
||||
this.network.cleanUp();
|
||||
this.blockchain.destroy();
|
||||
|
|
|
@ -4,6 +4,9 @@ var TxProposals = require('./TxProposals');
|
|||
var PublicKeyRing = require('./PublicKeyRing');
|
||||
var PrivateKey = require('./PrivateKey');
|
||||
var Wallet = require('./Wallet');
|
||||
var preconditions = require('preconditions').instance();
|
||||
|
||||
var log = require('../../log');
|
||||
|
||||
var Async = module.exports.Async = require('../network/Async');
|
||||
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
||||
|
@ -11,7 +14,6 @@ var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../s
|
|||
|
||||
/*
|
||||
* WalletFactory
|
||||
*
|
||||
*/
|
||||
|
||||
function WalletFactory(config, version) {
|
||||
|
@ -27,19 +29,10 @@ function WalletFactory(config, version) {
|
|||
this.blockchain = new this.Blockchain(config.blockchain);
|
||||
|
||||
this.networkName = config.networkName;
|
||||
this.verbose = config.verbose;
|
||||
this.walletDefaults = config.wallet;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
WalletFactory.prototype.log = function() {
|
||||
if (!this.verbose) return;
|
||||
if (console) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
WalletFactory.prototype._checkRead = function(walletId) {
|
||||
var s = this.storage;
|
||||
var ret =
|
||||
|
@ -112,7 +105,7 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
|||
|
||||
WalletFactory.prototype.create = function(opts) {
|
||||
opts = opts || {};
|
||||
this.log('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
||||
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
||||
|
||||
var privOpts = {
|
||||
networkName: this.networkName,
|
||||
|
@ -137,12 +130,12 @@ WalletFactory.prototype.create = function(opts) {
|
|||
opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(),
|
||||
opts.nickname
|
||||
);
|
||||
this.log('\t### PublicKeyRing Initialized');
|
||||
log.debug('\t### PublicKeyRing Initialized');
|
||||
|
||||
opts.txProposals = opts.txProposals || new TxProposals({
|
||||
networkName: this.networkName,
|
||||
});
|
||||
this.log('\t### TxProposals Initialized');
|
||||
log.debug('\t### TxProposals Initialized');
|
||||
|
||||
this.storage._setPassphrase(opts.passphrase);
|
||||
|
||||
|
@ -236,7 +229,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
|||
|
||||
//Create our PrivateK
|
||||
var privateKey = new PrivateKey(privOpts);
|
||||
this.log('\t### PrivateKey Initialized');
|
||||
log.debug('\t### PrivateKey Initialized');
|
||||
var opts = {
|
||||
copayerId: privateKey.getId(),
|
||||
privkey: privateKey.getIdPriv(),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var bitcore = require('bitcore');
|
||||
var log = require('../../log');
|
||||
var AuthMessage = bitcore.AuthMessage;
|
||||
var util = bitcore.util;
|
||||
var nodeUtil = require('util');
|
||||
|
@ -187,7 +188,7 @@ Network.prototype._onMessage = function(enc) {
|
|||
return;
|
||||
}
|
||||
|
||||
//console.log('receiving ' + JSON.stringify(payload));
|
||||
log.debug('receiving ' + JSON.stringify(payload));
|
||||
|
||||
var self = this;
|
||||
switch (payload.type) {
|
||||
|
@ -234,8 +235,8 @@ Network.prototype._setupConnectionHandlers = function(cb) {
|
|||
};
|
||||
|
||||
Network.prototype._onError = function(err) {
|
||||
console.log('RECV ERROR: ', err);
|
||||
console.log(err.stack);
|
||||
log.debug('RECV ERROR: ', err);
|
||||
log.debug(err.stack);
|
||||
this.criticalError = err.message;
|
||||
};
|
||||
|
||||
|
@ -348,7 +349,7 @@ Network.prototype.send = function(dest, payload, cb) {
|
|||
var to = dest[ii];
|
||||
if (to == this.copayerId)
|
||||
continue;
|
||||
//console.log('SEND to: ' + to, this.copayerId, payload);
|
||||
log.debug('SEND to: ' + to, this.copayerId, payload);
|
||||
var message = this.encode(to, payload);
|
||||
this.socket.emit('message', message);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
||||
var bitcore = require('bitcore');
|
||||
var preconditions = require('preconditions').instance();
|
||||
var id = 0;
|
||||
|
||||
function Storage(opts) {
|
||||
|
@ -11,15 +12,12 @@ function Storage(opts) {
|
|||
if (opts.password)
|
||||
this._setPassphrase(opts.password);
|
||||
|
||||
try{
|
||||
try {
|
||||
this.localStorage = opts.localStorage || localStorage;
|
||||
this.sessionStorage = opts.sessionStorage || sessionStorage;
|
||||
} catch (e) {};
|
||||
|
||||
if (!this.localStorage)
|
||||
throw new Error('no localStorage');
|
||||
if (!this.sessionStorage)
|
||||
throw new Error('no sessionStorage');
|
||||
} catch (e) {}
|
||||
preconditions.checkState(this.localStorage, 'No localstorage found');
|
||||
preconditions.checkState(this.sessionStorage, 'No sessionStorage found');
|
||||
}
|
||||
|
||||
var pps = {};
|
||||
|
@ -40,11 +38,6 @@ Storage.prototype._encrypt = function(string) {
|
|||
return encryptedBase64;
|
||||
};
|
||||
|
||||
Storage.prototype._encryptObj = function(obj) {
|
||||
var string = JSON.stringify(obj);
|
||||
return this._encrypt(string);
|
||||
};
|
||||
|
||||
Storage.prototype._decrypt = function(base64) {
|
||||
var decryptedStr = null;
|
||||
try {
|
||||
|
@ -58,10 +51,6 @@ Storage.prototype._decrypt = function(base64) {
|
|||
return decryptedStr;
|
||||
};
|
||||
|
||||
Storage.prototype._decryptObj = function(base64) {
|
||||
var decryptedStr = this._decrypt(base64);
|
||||
return JSON.parse(decryptedStr);
|
||||
};
|
||||
|
||||
Storage.prototype._read = function(k) {
|
||||
var ret;
|
||||
|
@ -98,7 +87,7 @@ Storage.prototype.removeGlobal = function(k) {
|
|||
};
|
||||
|
||||
Storage.prototype.getSessionId = function() {
|
||||
var sessionId = this.sessionStorage.getItem('sessionId');
|
||||
var sessionId = this.sessionStorage.getItem('sessionId');
|
||||
if (!sessionId) {
|
||||
sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex');
|
||||
this.sessionStorage.setItem('sessionId', sessionId);
|
||||
|
@ -131,7 +120,6 @@ Storage.prototype.setName = function(walletId, name) {
|
|||
|
||||
Storage.prototype.getName = function(walletId) {
|
||||
var ret = this.getGlobal('nameFor::' + walletId);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
@ -145,7 +133,7 @@ Storage.prototype.getWalletIds = function() {
|
|||
if (split.length == 2) {
|
||||
var walletId = split[0];
|
||||
|
||||
if (!walletId || walletId === 'nameFor' || walletId ==='lock')
|
||||
if (!walletId || walletId === 'nameFor' || walletId === 'lock')
|
||||
continue;
|
||||
|
||||
if (typeof uniq[walletId] === 'undefined') {
|
||||
|
@ -207,14 +195,14 @@ Storage.prototype.clearAll = function() {
|
|||
this.localStorage.clear();
|
||||
};
|
||||
|
||||
Storage.prototype.export = function(obj) {
|
||||
var encryptedObj = this._encryptObj(obj);
|
||||
return encryptedObj;
|
||||
Storage.prototype.import = function(base64) {
|
||||
var decryptedStr = this._decrypt(base64);
|
||||
return JSON.parse(decryptedStr);
|
||||
};
|
||||
|
||||
Storage.prototype.import = function(base64) {
|
||||
var decryptedObj = this._decryptObj(base64);
|
||||
return decryptedObj;
|
||||
Storage.prototype.export = function(obj) {
|
||||
var string = JSON.stringify(obj);
|
||||
return this._encrypt(string);
|
||||
};
|
||||
|
||||
module.exports = Storage;
|
||||
|
|
|
@ -22,8 +22,8 @@ angular
|
|||
templateUrl: 'views/import.html',
|
||||
validate: false
|
||||
})
|
||||
.when('/setup', {
|
||||
templateUrl: 'views/setup.html',
|
||||
.when('/create', {
|
||||
templateUrl: 'views/create.html',
|
||||
validate: false
|
||||
})
|
||||
.when('/copayers', {
|
||||
|
|
|
@ -2,17 +2,8 @@
|
|||
var bitcore = require('bitcore');
|
||||
|
||||
angular.module('copayApp.services')
|
||||
.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, video, uriHandler) {
|
||||
.factory('controllerUtils', function($rootScope, $sce, $location, notification, $timeout, uriHandler, rateService) {
|
||||
var root = {};
|
||||
root.getVideoMutedStatus = function(copayer) {
|
||||
if (!$rootScope.videoInfo) return;
|
||||
|
||||
var vi = $rootScope.videoInfo[copayer]
|
||||
if (!vi) {
|
||||
return;
|
||||
}
|
||||
return vi.muted;
|
||||
};
|
||||
|
||||
root.redirIfLogged = function() {
|
||||
if ($rootScope.wallet) {
|
||||
|
@ -27,7 +18,6 @@ angular.module('copayApp.services')
|
|||
$rootScope.wallet = null;
|
||||
delete $rootScope['wallet'];
|
||||
|
||||
video.close();
|
||||
// Clear rootScope
|
||||
for (var i in $rootScope) {
|
||||
if (i.charAt(0) != '$') {
|
||||
|
@ -102,18 +92,6 @@ angular.module('copayApp.services')
|
|||
root.installStartupHandlers(w, $scope);
|
||||
root.updateGlobalAddresses();
|
||||
|
||||
var handlePeerVideo = function(err, peerID, url) {
|
||||
if (err) {
|
||||
delete $rootScope.videoInfo[peerID];
|
||||
return;
|
||||
}
|
||||
$rootScope.videoInfo[peerID] = {
|
||||
url: encodeURI(url),
|
||||
muted: peerID === w.network.peerId
|
||||
};
|
||||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||
|
||||
w.on('corrupt', function(peerId) {
|
||||
|
@ -128,8 +106,6 @@ angular.module('copayApp.services')
|
|||
} else {
|
||||
$location.path('receive');
|
||||
}
|
||||
if (!config.disableVideo)
|
||||
video.setOwnPeer(myPeerID, w, handlePeerVideo);
|
||||
});
|
||||
|
||||
w.on('publicKeyRingUpdated', function(dontDigest) {
|
||||
|
@ -172,9 +148,6 @@ angular.module('copayApp.services')
|
|||
root.onErrorDigest(null, msg);
|
||||
});
|
||||
w.on('connect', function(peerID) {
|
||||
if (peerID && !config.disableVideo) {
|
||||
video.callPeer(peerID, handlePeerVideo);
|
||||
}
|
||||
$rootScope.$digest();
|
||||
});
|
||||
w.on('close', root.onErrorDigest);
|
||||
|
@ -217,7 +190,15 @@ angular.module('copayApp.services')
|
|||
$rootScope.balanceByAddr = balanceByAddr;
|
||||
root.updateAddressList();
|
||||
$rootScope.updatingBalance = false;
|
||||
return cb ? cb() : null;
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
$rootScope.totalBalanceAlternative = rateService.toFiat(balanceSat, config.alternativeIsoCode);
|
||||
$rootScope.alternativeIsoCode = config.alternativeIsoCode;
|
||||
$rootScope.lockedBalanceAlternative = rateService.toFiat(balanceSat - safeBalanceSat, config.alternativeIsoCode);
|
||||
|
||||
|
||||
return cb ? cb() : null;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
'use strict';
|
||||
|
||||
var RateService = function(request) {
|
||||
this.isAvailable = false;
|
||||
this.UNAVAILABLE_ERROR = 'Service is not available - check for service.isAvailable or use service.whenAvailable';
|
||||
this.SAT_TO_BTC = 1 / 1e8;
|
||||
var MINS_IN_HOUR = 60;
|
||||
var MILLIS_IN_SECOND = 1000;
|
||||
var rateServiceConfig = config.rate;
|
||||
var updateFrequencySeconds = rateServiceConfig.updateFrequencySeconds || 60 * MINS_IN_HOUR;
|
||||
var rateServiceUrl = rateServiceConfig.url || 'https://bitpay.com/api/rates';
|
||||
this.queued = [];
|
||||
this.alternatives = [];
|
||||
var that = this;
|
||||
var backoffSeconds = 5;
|
||||
var retrieve = function() {
|
||||
request.get({
|
||||
url: rateServiceUrl,
|
||||
json: true
|
||||
}, function(err, response, listOfCurrencies) {
|
||||
if (err) {
|
||||
backoffSeconds *= 1.5;
|
||||
setTimeout(retrieve, backoffSeconds * MILLIS_IN_SECOND);
|
||||
return;
|
||||
}
|
||||
var rates = {};
|
||||
listOfCurrencies.forEach(function(element) {
|
||||
rates[element.code] = element.rate;
|
||||
that.alternatives.push({
|
||||
name: element.name,
|
||||
isoCode: element.code,
|
||||
rate: element.rate
|
||||
});
|
||||
});
|
||||
that.isAvailable = true;
|
||||
that.rates = rates;
|
||||
that.queued.forEach(function(callback) {
|
||||
setTimeout(callback, 1);
|
||||
});
|
||||
setTimeout(retrieve, updateFrequencySeconds * MILLIS_IN_SECOND);
|
||||
});
|
||||
};
|
||||
retrieve();
|
||||
};
|
||||
|
||||
RateService.prototype.whenAvailable = function(callback) {
|
||||
if (this.isAvailable) {
|
||||
setTimeout(callback, 1);
|
||||
} else {
|
||||
this.queued.push(callback);
|
||||
}
|
||||
};
|
||||
|
||||
RateService.prototype.toFiat = function(satoshis, code) {
|
||||
if (!this.isAvailable) {
|
||||
throw new Error(this.UNAVAILABLE_ERROR);
|
||||
}
|
||||
return satoshis * this.SAT_TO_BTC * this.rates[code];
|
||||
};
|
||||
|
||||
RateService.prototype.fromFiat = function(amount, code) {
|
||||
if (!this.isAvailable) {
|
||||
throw new Error(this.UNAVAILABLE_ERROR);
|
||||
}
|
||||
return amount / this.rates[code] / this.SAT_TO_BTC;
|
||||
};
|
||||
|
||||
RateService.prototype.listAlternatives = function() {
|
||||
if (!this.isAvailable) {
|
||||
throw new Error(this.UNAVAILABLE_ERROR);
|
||||
}
|
||||
|
||||
var alts = [];
|
||||
this.alternatives.forEach(function(element) {
|
||||
alts.push({
|
||||
name: element.name,
|
||||
isoCode: element.isoCode
|
||||
});
|
||||
});
|
||||
return alts;
|
||||
};
|
||||
|
||||
angular.module('copayApp.services').service('rateService', RateService);
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').factory('request', function() {
|
||||
return require('request');
|
||||
});
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Video = function() {
|
||||
navigator.getUserMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia;
|
||||
|
||||
this.mediaConnections = {};
|
||||
this.localStream = null;
|
||||
};
|
||||
|
||||
Video.prototype.setOwnPeer = function(peer, wallet, cb) {
|
||||
var self = this;
|
||||
|
||||
var VWIDTH = 320;
|
||||
var VHEIGHT = 320;
|
||||
var constraints = {
|
||||
audio: true,
|
||||
video: {
|
||||
mandatory: {
|
||||
maxWidth: VWIDTH,
|
||||
maxHeight: VHEIGHT,
|
||||
}
|
||||
}
|
||||
};
|
||||
navigator.getUserMedia(constraints, function(stream) {
|
||||
// This is called when user accepts using webcam
|
||||
self.localStream = stream;
|
||||
var online = wallet.getOnlinePeerIDs();
|
||||
for (var i = 0; i < online.length; i++) {
|
||||
var o = online[i];
|
||||
if (o !== peer.id) {
|
||||
self.callPeer(o, cb);
|
||||
}
|
||||
}
|
||||
cb(null, peer.id, URL.createObjectURL(stream));
|
||||
}, function() {
|
||||
cb(new Error('Failed to access the webcam and microphone.'));
|
||||
});
|
||||
|
||||
// Receiving a call
|
||||
peer.on('call', function(mediaConnection) {
|
||||
if (self.localStream) {
|
||||
mediaConnection.answer(self.localStream);
|
||||
} else {
|
||||
mediaConnection.answer();
|
||||
}
|
||||
self._addCall(mediaConnection, cb);
|
||||
});
|
||||
this.peer = peer;
|
||||
};
|
||||
|
||||
Video.prototype.callPeer = function(peerID, cb) {
|
||||
if (this.localStream) {
|
||||
var mediaConnection = this.peer.call(peerID, this.localStream);
|
||||
this._addCall(mediaConnection, cb);
|
||||
}
|
||||
};
|
||||
|
||||
Video.prototype._addCall = function(mediaConnection, cb) {
|
||||
var self = this;
|
||||
var peerID = mediaConnection.peer;
|
||||
|
||||
// Wait for stream on the call, then set peer video display
|
||||
mediaConnection.on('stream', function(stream) {
|
||||
cb(null, peerID, URL.createObjectURL(stream));
|
||||
});
|
||||
|
||||
mediaConnection.on('close', function() {
|
||||
cb(true, peerID, null); // ask to stop video streaming in UI
|
||||
});
|
||||
mediaConnection.on('error', function(e) {
|
||||
cb(e, peerID, null);
|
||||
});
|
||||
this.mediaConnections[peerID] = mediaConnection;
|
||||
}
|
||||
|
||||
Video.prototype.close = function() {
|
||||
if (this.localStream) {
|
||||
this.localStream.stop();
|
||||
this.localStream.mozSrcObject = null;
|
||||
this.localStream.src = "";
|
||||
this.localStream.src = null;
|
||||
this.localStream = null;
|
||||
}
|
||||
for (var i = 0; this.mediaConnections.length; i++) {
|
||||
this.mediaConnections[i].close();
|
||||
}
|
||||
this.mediaConnections = {};
|
||||
};
|
||||
|
||||
angular.module('copayApp.services').value('video', new Video());
|
|
@ -28,7 +28,6 @@ module.exports = function(config) {
|
|||
'lib/angular-route/angular-route.min.js',
|
||||
'lib/angular-foundation/mm-foundation.min.js',
|
||||
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
||||
'lib/peerjs/peer.js',
|
||||
'lib/bitcore.js',
|
||||
'lib/crypto-js/rollups/sha256.js',
|
||||
'lib/crypto-js/rollups/pbkdf2.js',
|
||||
|
@ -42,6 +41,7 @@ module.exports = function(config) {
|
|||
|
||||
//App-specific Code
|
||||
'js/app.js',
|
||||
'js/log.js',
|
||||
'js/routes.js',
|
||||
'js/services/*.js',
|
||||
'js/directives.js',
|
||||
|
|
2657
lib/peer.js
2657
lib/peer.js
File diff suppressed because it is too large
Load Diff
|
@ -72,7 +72,9 @@
|
|||
"travis-cov": "0.2.5",
|
||||
"uglifyify": "1.2.3",
|
||||
"crypto-js": "3.1.2",
|
||||
"shelljs": "0.3.0"
|
||||
"shelljs":"0.3.0",
|
||||
"browser-request": "0.3.2",
|
||||
"request": "2.40.0"
|
||||
},
|
||||
"main": "app.js",
|
||||
"homepage": "https://github.com/bitpay/copay",
|
||||
|
|
|
@ -18,7 +18,7 @@ describe('Passphrase model', function() {
|
|||
should.exist(p);
|
||||
});
|
||||
|
||||
it('should generate key from password', function () {
|
||||
it('should generate key from password', function (done) {
|
||||
var p = new Passphrase({
|
||||
salt: 'mjuBtGybi/4=',
|
||||
iterations: 10,
|
||||
|
@ -33,6 +33,7 @@ describe('Passphrase model', function() {
|
|||
|
||||
p.getBase64Async(pass, function (ret) {
|
||||
ret.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -29,7 +29,9 @@ describe("Unit: Controllers", function() {
|
|||
totalCopayers: 5,
|
||||
spendUnconfirmed: 1,
|
||||
reconnectDelay: 100,
|
||||
networkName: 'testnet'
|
||||
networkName: 'testnet',
|
||||
alternativeName: 'lol currency',
|
||||
alternativeIsoCode: 'LOL'
|
||||
};
|
||||
|
||||
it('Copay config should be binded', function() {
|
||||
|
@ -65,11 +67,11 @@ describe("Unit: Controllers", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('Setup Controller', function() {
|
||||
var setupCtrl;
|
||||
describe('Create Controller', function() {
|
||||
var c;
|
||||
beforeEach(inject(function($controller, $rootScope) {
|
||||
scope = $rootScope.$new();
|
||||
setupCtrl = $controller('SetupController', {
|
||||
c = $controller('CreateController', {
|
||||
$scope: scope,
|
||||
});
|
||||
}));
|
||||
|
@ -124,11 +126,21 @@ describe("Unit: Controllers", function() {
|
|||
});
|
||||
|
||||
describe('Send Controller', function() {
|
||||
var scope, form, sendForm;
|
||||
var scope, form, sendForm, sendCtrl;
|
||||
beforeEach(angular.mock.module('copayApp'));
|
||||
beforeEach(angular.mock.inject(function($compile, $rootScope, $controller) {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('request', {
|
||||
'get': function(_, cb) {
|
||||
cb(null, null, [{name: 'lol currency', code: 'LOL', rate: 2}]);
|
||||
}
|
||||
});
|
||||
}));
|
||||
beforeEach(angular.mock.inject(function($compile, $rootScope, $controller, rateService) {
|
||||
scope = $rootScope.$new();
|
||||
scope.rateService = rateService;
|
||||
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||
config.alternativeName = 'lol currency';
|
||||
config.alternativeIsoCode = 'LOL';
|
||||
var element = angular.element(
|
||||
'<form name="form">' +
|
||||
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
||||
|
@ -147,11 +159,12 @@ describe("Unit: Controllers", function() {
|
|||
'<form name="form2">' +
|
||||
'<input type="text" id="address" name="address" ng-model="address" valid-address required>' +
|
||||
'<input type="number" id="amount" name="amount" ng-model="amount" min="1" max="10000000000" required>' +
|
||||
'<input type="number" id="alternative" name="alternative" ng-model="alternative">' +
|
||||
'<textarea id="comment" name="comment" ng-model="commentText" ng-maxlength="100"></textarea>' +
|
||||
'</form>'
|
||||
);
|
||||
$compile(element2)(scope);
|
||||
$controller('SendController', {
|
||||
sendCtrl = $controller('SendController', {
|
||||
$scope: scope,
|
||||
$modal: {},
|
||||
});
|
||||
|
@ -241,8 +254,22 @@ describe("Unit: Controllers", function() {
|
|||
config.unitToSatoshi = old;
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should convert bits amount to fiat', function(done) {
|
||||
scope.rateService.whenAvailable(function() {
|
||||
sendForm.amount.$setViewValue(1e6);
|
||||
scope.$digest();
|
||||
expect(scope.alternative).to.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should convert fiat to bits amount', function(done) {
|
||||
scope.rateService.whenAvailable(function() {
|
||||
sendForm.alternative.$setViewValue(2);
|
||||
scope.$digest();
|
||||
expect(scope.amount).to.equal(1e6);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create and send a transaction proposal', function() {
|
||||
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||
|
|
|
@ -79,8 +79,6 @@ describe("Unit: controllerUtils", function() {
|
|||
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("Unit: Notification Service", function() {
|
||||
|
@ -118,12 +116,7 @@ describe("Unit: isMobile Service", function() {
|
|||
isMobile.any().should.equal(true);
|
||||
}));
|
||||
});
|
||||
describe("Unit: video service", function() {
|
||||
beforeEach(angular.mock.module('copayApp.services'));
|
||||
it('should contain a video service', inject(function(video) {
|
||||
should.exist(video);
|
||||
}));
|
||||
});
|
||||
|
||||
describe("Unit: uriHandler service", function() {
|
||||
beforeEach(angular.mock.module('copayApp.services'));
|
||||
it('should contain a uriHandler service', inject(function(uriHandler) {
|
||||
|
@ -135,3 +128,36 @@ describe("Unit: uriHandler service", function() {
|
|||
}).should.not.throw();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('Unit: Rate Service', function() {
|
||||
beforeEach(angular.mock.module('copayApp.services'));
|
||||
it('should be injected correctly', inject(function(rateService) {
|
||||
should.exist(rateService);
|
||||
}));
|
||||
it('should be possible to ask if it is available',
|
||||
inject(function(rateService) {
|
||||
should.exist(rateService.isAvailable);
|
||||
})
|
||||
);
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.value('request', {
|
||||
'get': function(_, cb) {
|
||||
cb(null, null, [{name: 'lol currency', code: 'LOL', rate: 2}]);
|
||||
}
|
||||
});
|
||||
}));
|
||||
it('should be possible to ask for conversion from fiat',
|
||||
inject(function(rateService) {
|
||||
rateService.whenAvailable(function() {
|
||||
(1).should.equal(rateService.fromFiat(2, 'LOL'));
|
||||
});
|
||||
})
|
||||
);
|
||||
it('should be possible to ask for conversion to fiat',
|
||||
inject(function(rateService) {
|
||||
rateService.whenAvailable(function() {
|
||||
(2).should.equal(rateService.toFiat(1e8, 'LOL'));
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
|
@ -51,10 +51,10 @@
|
|||
|
||||
<div class="box-setup-copayers p20">
|
||||
<div class="oh">
|
||||
<div ng-include="'views/includes/video.html'"></div>
|
||||
<div ng-include="'views/includes/copayer.html'"></div>
|
||||
<div ng-if="!$root.wallet.publicKeyRing.isComplete()">
|
||||
<img
|
||||
class="waiting br100 no-video"
|
||||
class="waiting br100"
|
||||
ng-if="!hasVideo(copayer)"
|
||||
src="./img/satoshi.gif"
|
||||
alt="Waiting Copayer"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div ng-controller="SetupController">
|
||||
<div ng-controller="CreateController">
|
||||
<div data-alert class="loading-screen" ng-show="loading">
|
||||
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
|
||||
Creating wallet...
|
|
@ -9,13 +9,13 @@
|
|||
<a class="text-white" href="#!/open">Open a wallet</a>
|
||||
</div>
|
||||
<div class="button-setup" ng-show="!hasWallets">
|
||||
<a class="text-secondary" href="#!/setup">Create a new wallet</a>
|
||||
<a class="text-secondary" href="#!/create">Create a new wallet</a>
|
||||
</div>
|
||||
<div class="button-setup">
|
||||
<a class="text-primary" href="#!/join">Join a Wallet in Creation</a>
|
||||
</div>
|
||||
<div class="button-setup" ng-show="hasWallets">
|
||||
<a class="text-secondary" href="#!/setup">Create a wallet</a>
|
||||
<a class="text-secondary" href="#!/create">Create a wallet</a>
|
||||
</div>
|
||||
|
||||
<div class="footer-setup">
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
<div ng-controller="VideoController">
|
||||
<div class="video-box" ng-repeat="copayer in copayersList()">
|
||||
<video
|
||||
ng-if="hasVideo(copayer)"
|
||||
peer="{{copayer}}" avatar autoplay
|
||||
ng-class="true || isConnected(copayer) ? 'online' : 'offline'"
|
||||
ng-src="{{getVideoURL(copayer)}}"></video>
|
||||
|
||||
<div ng-controller="CopayersController">
|
||||
<div class="copay-box" ng-repeat="copayer in copayersList()">
|
||||
<img
|
||||
class="br100 no-video"
|
||||
ng-if="!hasVideo(copayer)"
|
||||
ng-class="true || isConnected(copayer) ? 'online' : 'offline'"
|
||||
class="br100 online"
|
||||
src="./img/satoshi.gif"
|
||||
alt="{{copayer}}"
|
||||
alt="{{copayer.peerId}}"
|
||||
width="70">
|
||||
|
||||
<div
|
|
@ -1,55 +1,22 @@
|
|||
<div ng-controller="VideoController" class="copayers">
|
||||
<div>
|
||||
<h3>
|
||||
<i class="fi-torsos-all size-21 m20r"></i>
|
||||
Copayers
|
||||
<small class="m15l">
|
||||
{{$root.wallet.requiredCopayers}} of {{$root.wallet.totalCopayers}}
|
||||
</small>
|
||||
</h3>
|
||||
</div>
|
||||
<div ng-controller="CopayersController" class="copayers">
|
||||
<h3>
|
||||
<i class="fi-torsos-all size-21 m20r"></i>
|
||||
Copayers
|
||||
<small class="m15l">
|
||||
{{$root.wallet.requiredCopayers}} of {{$root.wallet.totalCopayers}}
|
||||
</small>
|
||||
</h3>
|
||||
|
||||
<div class="copay-box-small" ng-repeat="copayer in copayers">
|
||||
<img
|
||||
class="br100 online"
|
||||
src="./img/satoshi.gif"
|
||||
alt="{{copayer.peerId}}"
|
||||
width="30">
|
||||
|
||||
<ul class="copayer-list" ng-repeat="copayer in copayers">
|
||||
<li class="ellipsis">
|
||||
<video
|
||||
ng-if="hasVideo(copayer)"
|
||||
peer="{{copayer}}" avatar autoplay
|
||||
ng-class="isConnected(copayer) ? 'online' : 'offline'"
|
||||
ng-src="{{getVideoURL(copayer)}}"></video>
|
||||
|
||||
<img
|
||||
class="br100 oh no-video m20r"
|
||||
ng-if="!hasVideo(copayer)"
|
||||
ng-class="isConnected(copayer) ? 'online' : 'offline'"
|
||||
src="./img/satoshi.gif"
|
||||
alt="{{copayer}}"
|
||||
width="70">
|
||||
|
||||
<span ng-show="copayer.index == 0">Me</span>
|
||||
<span ng-show="copayer.index > 0">{{copayer.nick}}</span>
|
||||
<span class="btn-copy" clip-copy="copayer.peerId"></span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="copayer-list-small-height" ng-repeat="copayer in copayers">
|
||||
<span class="left" class="has-tip" tooltip-placement="top" tooltip="{{copayer.nick}}">
|
||||
<video
|
||||
ng-if="hasVideo(copayer)"
|
||||
peer="{{copayer}}" avatar autoplay
|
||||
ng-class="isConnected(copayer) ? 'online' : 'offline'"
|
||||
ng-src="{{getVideoURL(copayer)}}"></video>
|
||||
|
||||
<img
|
||||
class="br100 oh no-video m20r"
|
||||
ng-if="!hasVideo(copayer)"
|
||||
ng-class="isConnected(copayer) ? 'online' : 'offline'"
|
||||
src="./img/satoshi.gif"
|
||||
alt="{{copayer}}"
|
||||
width="70">
|
||||
|
||||
<!-- <span ng-show="copayer.index == 0">Me</span>
|
||||
<span ng-show="copayer.index > 0">{{copayer.nick}}</span>
|
||||
<span class="btn-copy" clip-copy="copayer.peerId"></span> -->
|
||||
</span>
|
||||
<div class="ellipsis" tooltip-placement="top" tooltip="{{copayer.nick}}">
|
||||
<small class="text-gray" ng-show="copayer.index == 0">Me</small>
|
||||
<small class="text-gray" ng-show="copayer.index > 0">{{copayer.nick}}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -24,10 +24,9 @@
|
|||
class="has-tip"
|
||||
data-options="disable_for_touch:true"
|
||||
tooltip-popup-delay='500'
|
||||
tooltip="{{totalBalanceBTC |noFractionNumber:8}} BTC"
|
||||
tooltip="{{totalBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||
tooltip-trigger="mouseenter"
|
||||
tooltip-placement="bottom">{{totalBalance || 0
|
||||
|noFractionNumber}} {{$root.unitName}}
|
||||
tooltip-placement="bottom">{{totalBalance || 0 |noFractionNumber}} {{$root.unitName}}
|
||||
</span>
|
||||
<br>
|
||||
Locked
|
||||
|
@ -38,7 +37,7 @@
|
|||
class="has-tip"
|
||||
data-options="disable_for_touch:true"
|
||||
tooltip-popup-delay='500'
|
||||
tooltip="{{lockedBalanceBTC |noFractionNumber:8}} BTC"
|
||||
tooltip="{{lockedBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||
tooltip-trigger="mouseenter"
|
||||
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
||||
</span> <i class="fi-info medium" tooltip="Balance locked in pending transaction proposals" tooltip-placement="bottom"></i>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<div class="backup" ng-controller="MoreController">
|
||||
<h1>Backup & Delete </h1>
|
||||
<h1>Settings </h1>
|
||||
<div class="oh large-12 columns panel">
|
||||
<h3><i class="fi-download m10r"></i> Backup </h3>
|
||||
<p class="large-8 columns text-gray"> Its important to back up your wallet so that you can recover your wallet in case of disaster </p>
|
||||
<p class="large-8 columns text-gray"> It's important to backup your wallet so that you can recover it in case of disaster</p>
|
||||
<div class="large-4 columns">
|
||||
<a class="button primary expand" ng-click="downloadBackup()">Download File</a>
|
||||
</div>
|
||||
|
|
|
@ -83,6 +83,22 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="large-6 medium-6 columns">
|
||||
<div class="row collapse">
|
||||
<label for="alternative">Amount in {{ alternativeName }} </label>
|
||||
<div class="small-9 columns">
|
||||
<input type="number" id="alternative_amount"
|
||||
ng-disabled="loading || !isRateAvailable "
|
||||
name="alternative" placeholder="Amount" ng-model="alternative"
|
||||
required
|
||||
autocomplete="off"
|
||||
>
|
||||
</div>
|
||||
<div class="small-3 columns">
|
||||
<span class="postfix">{{alternativeIsoCode}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-show="wallet.isShared()">
|
||||
|
@ -110,7 +126,7 @@
|
|||
</form>
|
||||
</div>
|
||||
|
||||
<div class="large-6 columns show-for-large-up">
|
||||
<div class="large-6 columns show-for-large-up" ng-show="!!$root.merchant">
|
||||
<div class="send-note">
|
||||
<h6>Send to</h6>
|
||||
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
||||
|
@ -119,8 +135,11 @@
|
|||
<h6>Total amount for this transaction:</h6>
|
||||
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
||||
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
||||
<small ng-if="isRateAvailable">
|
||||
{{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }}
|
||||
<br>
|
||||
</small>
|
||||
<small>
|
||||
{{ ((amount + defaultFee) * unitToBtc)|noFractionNumber:8}} BTC <br/>
|
||||
Including fee of {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
||||
</small>
|
||||
</p>
|
||||
|
|
|
@ -27,9 +27,9 @@
|
|||
</select>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Videoconferencing</legend>
|
||||
<input id="disableVideo-opt" type="checkbox" ng-model="disableVideo" class="form-control">
|
||||
<label for="disableVideo-opt">Disable videoconferencing (for slow networks)</label>
|
||||
<legend>Alternative Currency</legend>
|
||||
<select class="form-control" ng-model="selectedAlternative" ng-options="alternative.name for alternative in alternativeOpts" required>
|
||||
</select>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Insight API server</legend>
|
||||
|
|
Loading…
Reference in New Issue