Merge branch 'master' into bugs/ui
This commit is contained in:
commit
2d33dcfef6
40 changed files with 520 additions and 3158 deletions
|
|
@ -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
|
'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/moment/min/moment.min.js',
|
||||||
'lib/qrcode-generator/js/qrcode.js',
|
'lib/qrcode-generator/js/qrcode.js',
|
||||||
'lib/peer.js',
|
|
||||||
'lib/bitcore.js',
|
'lib/bitcore.js',
|
||||||
'lib/crypto-js/rollups/sha256.js',
|
'lib/crypto-js/rollups/sha256.js',
|
||||||
'lib/crypto-js/rollups/pbkdf2.js',
|
'lib/crypto-js/rollups/pbkdf2.js',
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@
|
||||||
"angular-foundation": "*",
|
"angular-foundation": "*",
|
||||||
"angular-route": "~1.2.14",
|
"angular-route": "~1.2.14",
|
||||||
"angular-qrcode": "~3.1.0",
|
"angular-qrcode": "~3.1.0",
|
||||||
"peerjs": "=0.3.8",
|
|
||||||
"angular-mocks": "~1.2.14",
|
"angular-mocks": "~1.2.14",
|
||||||
"mocha": "~1.18.2",
|
"mocha": "~1.18.2",
|
||||||
"chai": "~1.9.1",
|
"chai": "~1.9.1",
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ var defaultConfig = {
|
||||||
// DEFAULT network (livenet or testnet)
|
// DEFAULT network (livenet or testnet)
|
||||||
networkName: 'testnet',
|
networkName: 'testnet',
|
||||||
forceNetwork: false,
|
forceNetwork: false,
|
||||||
|
logLevel: 'info',
|
||||||
|
|
||||||
// DEFAULT unit: Bit
|
// DEFAULT unit: Bit
|
||||||
unitName: 'bits',
|
unitName: 'bits',
|
||||||
unitToSatoshi: 100,
|
unitToSatoshi: 100,
|
||||||
|
alternativeName: 'US Dollar',
|
||||||
|
alternativeIsoCode: 'USD',
|
||||||
|
|
||||||
// wallet limits
|
// wallet limits
|
||||||
limits: {
|
limits: {
|
||||||
|
|
@ -54,7 +57,11 @@ var defaultConfig = {
|
||||||
storageSalt: 'mjuBtGybi/4=',
|
storageSalt: 'mjuBtGybi/4=',
|
||||||
},
|
},
|
||||||
|
|
||||||
disableVideo: true,
|
rate: {
|
||||||
|
url: 'https://bitpay.com/api/rates',
|
||||||
|
updateFrequencySeconds: 60 * 60
|
||||||
|
},
|
||||||
|
|
||||||
verbose: 1,
|
verbose: 1,
|
||||||
};
|
};
|
||||||
if (typeof module !== 'undefined')
|
if (typeof module !== 'undefined')
|
||||||
|
|
|
||||||
|
|
@ -205,23 +205,6 @@ a:hover {
|
||||||
color: #fff;
|
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 {
|
.button.small.side-bar {
|
||||||
padding: 0rem 0.4rem;
|
padding: 0rem 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
@ -954,7 +937,15 @@ button, .button, p {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-box {
|
.copay-box-small {
|
||||||
|
width: 40px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copay-box {
|
||||||
width: 70px;
|
width: 70px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
|
@ -962,11 +953,6 @@ button, .button, p {
|
||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-small {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-input {
|
.icon-input {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 11px;
|
top: 11px;
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,6 @@ if (localConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var log = function() {
|
|
||||||
if (config.verbose) console.log(arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var copayApp = window.copayApp = angular.module('copayApp', [
|
var copayApp = window.copayApp = angular.module('copayApp', [
|
||||||
'ngRoute',
|
'ngRoute',
|
||||||
'angularMoment',
|
'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
|
'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) {
|
function($scope, $rootScope, $location, $timeout, walletFactory, controllerUtils, Passphrase, backupService, notification) {
|
||||||
controllerUtils.redirIfLogged();
|
controllerUtils.redirIfLogged();
|
||||||
|
|
||||||
$rootScope.fromSetup = true;
|
$rootScope.fromSetup = true;
|
||||||
$rootScope.videoInfo = {};
|
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.walletPassword = $rootScope.walletPassword;
|
$scope.walletPassword = $rootScope.walletPassword;
|
||||||
$scope.isMobile = !!window.cordova;
|
$scope.isMobile = !!window.cordova;
|
||||||
|
|
@ -2,14 +2,67 @@
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
|
||||||
angular.module('copayApp.controllers').controller('SendController',
|
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.title = 'Send';
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
var satToUnit = 1 / config.unitToSatoshi;
|
var satToUnit = 1 / config.unitToSatoshi;
|
||||||
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
$scope.defaultFee = bitcore.TransactionBuilder.FEE_PER_1000B_SAT * satToUnit;
|
||||||
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
$scope.unitToBtc = config.unitToSatoshi / bitcore.util.COIN;
|
||||||
|
$scope.unitToSatoshi = config.unitToSatoshi;
|
||||||
$scope.minAmount = config.limits.minAmountSatoshi * satToUnit;
|
$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() {
|
$scope.loadTxs = function() {
|
||||||
var opts = {
|
var opts = {
|
||||||
pending: true,
|
pending: true,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
'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();
|
controllerUtils.redirIfLogged();
|
||||||
$scope.title = 'Settings';
|
$scope.title = 'Settings';
|
||||||
|
|
@ -8,27 +8,46 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
||||||
$scope.insightHost = config.blockchain.host;
|
$scope.insightHost = config.blockchain.host;
|
||||||
$scope.insightPort = config.blockchain.port;
|
$scope.insightPort = config.blockchain.port;
|
||||||
$scope.insightSecure = config.blockchain.schema === 'https';
|
$scope.insightSecure = config.blockchain.schema === 'https';
|
||||||
$scope.disableVideo = typeof config.disableVideo === undefined ? true : config.disableVideo;
|
|
||||||
$scope.forceNetwork = config.forceNetwork;
|
$scope.forceNetwork = config.forceNetwork;
|
||||||
|
|
||||||
$scope.unitOpts = [{
|
$scope.unitOpts = [{
|
||||||
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||||
shortName: 'SAT',
|
shortName: 'SAT',
|
||||||
value: 1
|
value: 1,
|
||||||
|
decimals: 0
|
||||||
}, {
|
}, {
|
||||||
name: 'bits (1,000,000 bits = 1BTC)',
|
name: 'bits (1,000,000 bits = 1BTC)',
|
||||||
shortName: 'bits',
|
shortName: 'bits',
|
||||||
value: 100
|
value: 100,
|
||||||
|
decimals: 2
|
||||||
}, {
|
}, {
|
||||||
name: 'mBTC (1,000 mBTC = 1BTC)',
|
name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||||
shortName: 'mBTC',
|
shortName: 'mBTC',
|
||||||
value: 100000
|
value: 100000,
|
||||||
|
decimals: 5
|
||||||
}, {
|
}, {
|
||||||
name: 'BTC',
|
name: 'BTC',
|
||||||
shortName: '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) {
|
for (var ii in $scope.unitOpts) {
|
||||||
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
if (config.unitName === $scope.unitOpts[ii].shortName) {
|
||||||
$scope.selectedUnit = $scope.unitOpts[ii];
|
$scope.selectedUnit = $scope.unitOpts[ii];
|
||||||
|
|
@ -65,10 +84,13 @@ angular.module('copayApp.controllers').controller('SettingsController', function
|
||||||
schema: $scope.insightSecure ? 'https' : 'http',
|
schema: $scope.insightSecure ? 'https' : 'http',
|
||||||
},
|
},
|
||||||
network: network,
|
network: network,
|
||||||
disableVideo: $scope.disableVideo,
|
|
||||||
unitName: $scope.selectedUnit.shortName,
|
unitName: $scope.selectedUnit.shortName,
|
||||||
unitToSatoshi: $scope.selectedUnit.value,
|
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
|
// Go home reloading the application
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
|
||||||
|
|
||||||
$scope.menu = [{
|
$scope.menu = [{
|
||||||
'title': 'Receive',
|
'title': 'Receive',
|
||||||
'icon': 'fi-arrow-left',
|
'icon': 'fi-download',
|
||||||
'link': 'receive'
|
'link': 'receive'
|
||||||
}, {
|
}, {
|
||||||
'title': 'Send',
|
'title': 'Send',
|
||||||
|
|
@ -15,8 +15,8 @@ angular.module('copayApp.controllers').controller('SidebarController', function(
|
||||||
'icon': 'fi-clipboard-pencil',
|
'icon': 'fi-clipboard-pencil',
|
||||||
'link': 'history'
|
'link': 'history'
|
||||||
}, {
|
}, {
|
||||||
'title': 'More',
|
'title': 'Settings',
|
||||||
'icon': 'fi-download',
|
'icon': 'fi-widget',
|
||||||
'link': 'more'
|
'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) {
|
link: function(scope, element, attrs, ctrl) {
|
||||||
var val = function(value) {
|
var val = function(value) {
|
||||||
var availableBalanceNum = Number(($rootScope.availableBalance * config.unitToSatoshi).toFixed(0));
|
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) {
|
if (typeof vNum == "number" && vNum > 0) {
|
||||||
|
vNum = vNum + feeSat;
|
||||||
if (availableBalanceNum < vNum || isNaN(availableBalanceNum)) {
|
if (availableBalanceNum < vNum || isNaN(availableBalanceNum)) {
|
||||||
ctrl.$setValidity('enoughAmount', false);
|
ctrl.$setValidity('enoughAmount', false);
|
||||||
scope.notEnoughAmount = true;
|
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() {
|
.directive('contact', function() {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
|
|
|
||||||
43
js/log.js
Normal file
43
js/log.js
Normal file
|
|
@ -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,
|
keySize: 512 / 32,
|
||||||
iterations: this.iterations
|
iterations: this.iterations
|
||||||
});
|
});
|
||||||
|
|
||||||
return key512;
|
return key512;
|
||||||
};
|
};
|
||||||
|
|
||||||
Passphrase.prototype.getBase64 = function(password) {
|
Passphrase.prototype.getBase64 = function(password) {
|
||||||
var key512 = this.get(password);
|
var key512 = this.get(password);
|
||||||
var keyBase64 = key512.toString(CryptoJS.enc.Base64);
|
var keyBase64 = key512.toString(CryptoJS.enc.Base64);
|
||||||
|
|
||||||
return keyBase64;
|
return keyBase64;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ var Base58Check = bitcore.Base58.base58Check;
|
||||||
var Address = bitcore.Address;
|
var Address = bitcore.Address;
|
||||||
var PayPro = bitcore.PayPro;
|
var PayPro = bitcore.PayPro;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
|
var log = require('../../log');
|
||||||
|
|
||||||
var HDParams = require('./HDParams');
|
var HDParams = require('./HDParams');
|
||||||
var PublicKeyRing = require('./PublicKeyRing');
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
|
|
@ -72,12 +73,6 @@ Wallet.builderOpts = {
|
||||||
feeSat: null,
|
feeSat: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.log = function() {
|
|
||||||
if (!this.verbose) return;
|
|
||||||
if (console)
|
|
||||||
console.log.apply(console, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
Wallet.getRandomId = function() {
|
Wallet.getRandomId = function() {
|
||||||
var r = bitcore.SecureRandom.getPseudoRandomBuffer(8).toString('hex');
|
var r = bitcore.SecureRandom.getPseudoRandomBuffer(8).toString('hex');
|
||||||
return r;
|
return r;
|
||||||
|
|
@ -88,7 +83,7 @@ Wallet.prototype.seedCopayer = function(pubKey) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype._onIndexes = function(senderId, data) {
|
Wallet.prototype._onIndexes = function(senderId, data) {
|
||||||
this.log('RECV INDEXES:', data);
|
log.debug('RECV INDEXES:', data);
|
||||||
var inIndexes = HDParams.fromList(data.indexes);
|
var inIndexes = HDParams.fromList(data.indexes);
|
||||||
var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes);
|
var hasChanged = this.publicKeyRing.mergeIndexes(inIndexes);
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
|
|
@ -98,7 +93,7 @@ Wallet.prototype._onIndexes = function(senderId, data) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype._onPublicKeyRing = 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 inPKR = PublicKeyRing.fromObj(data.publicKeyRing);
|
||||||
var wasIncomplete = !this.publicKeyRing.isComplete();
|
var wasIncomplete = !this.publicKeyRing.isComplete();
|
||||||
|
|
@ -107,7 +102,7 @@ Wallet.prototype._onPublicKeyRing = function(senderId, data) {
|
||||||
try {
|
try {
|
||||||
hasChanged = this.publicKeyRing.merge(inPKR, true);
|
hasChanged = this.publicKeyRing.merge(inPKR, true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.log('## WALLET ERROR', e);
|
log.debug('## WALLET ERROR', e);
|
||||||
this.emit('connectionError', e.message);
|
this.emit('connectionError', e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +200,7 @@ Wallet.prototype._checkSentTx = function(ntxid, cb) {
|
||||||
|
|
||||||
Wallet.prototype._onTxProposal = function(senderId, data) {
|
Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.log('RECV TXPROPOSAL: ', data);
|
log.debug('RECV TXPROPOSAL: ', data);
|
||||||
var m;
|
var m;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -214,7 +209,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||||
ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
|
ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.log('Corrupt TX proposal received from:', senderId, e);
|
log.debug('Corrupt TX proposal received from:', senderId, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m) {
|
if (m) {
|
||||||
|
|
@ -242,7 +237,7 @@ Wallet.prototype._onTxProposal = function(senderId, data) {
|
||||||
|
|
||||||
Wallet.prototype._onReject = function(senderId, data) {
|
Wallet.prototype._onReject = function(senderId, data) {
|
||||||
preconditions.checkState(data.ntxid);
|
preconditions.checkState(data.ntxid);
|
||||||
this.log('RECV REJECT:', data);
|
log.debug('RECV REJECT:', data);
|
||||||
|
|
||||||
var txp = this.txProposals.get(data.ntxid);
|
var txp = this.txProposals.get(data.ntxid);
|
||||||
|
|
||||||
|
|
@ -265,7 +260,7 @@ Wallet.prototype._onReject = function(senderId, data) {
|
||||||
|
|
||||||
Wallet.prototype._onSeen = function(senderId, data) {
|
Wallet.prototype._onSeen = function(senderId, data) {
|
||||||
preconditions.checkState(data.ntxid);
|
preconditions.checkState(data.ntxid);
|
||||||
this.log('RECV SEEN:', data);
|
log.debug('RECV SEEN:', data);
|
||||||
|
|
||||||
var txp = this.txProposals.get(data.ntxid);
|
var txp = this.txProposals.get(data.ntxid);
|
||||||
txp.setSeen(senderId);
|
txp.setSeen(senderId);
|
||||||
|
|
@ -283,7 +278,7 @@ Wallet.prototype._onSeen = function(senderId, data) {
|
||||||
|
|
||||||
Wallet.prototype._onAddressBook = function(senderId, data) {
|
Wallet.prototype._onAddressBook = function(senderId, data) {
|
||||||
preconditions.checkState(data.addressBook);
|
preconditions.checkState(data.addressBook);
|
||||||
this.log('RECV ADDRESSBOOK:', data);
|
log.debug('RECV ADDRESSBOOK:', data);
|
||||||
var rcv = data.addressBook;
|
var rcv = data.addressBook;
|
||||||
var hasChange;
|
var hasChange;
|
||||||
for (var key in rcv) {
|
for (var key in rcv) {
|
||||||
|
|
@ -311,7 +306,7 @@ Wallet.prototype.updateTimestamp = function(ts) {
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype._onNoMessages = function() {
|
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();
|
this.sendWalletReady();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -322,7 +317,7 @@ Wallet.prototype._onData = function(senderId, data, ts) {
|
||||||
preconditions.checkArgument(ts);
|
preconditions.checkArgument(ts);
|
||||||
preconditions.checkArgument(typeof ts === 'number');
|
preconditions.checkArgument(typeof ts === 'number');
|
||||||
|
|
||||||
console.log('RECV', senderId, data);
|
log.debug('RECV', senderId, data);
|
||||||
|
|
||||||
if (data.type !== 'walletId' && this.id !== data.walletId) {
|
if (data.type !== 'walletId' && this.id !== data.walletId) {
|
||||||
this.emit('corrupt', senderId);
|
this.emit('corrupt', senderId);
|
||||||
|
|
@ -375,7 +370,7 @@ Wallet.prototype._onData = function(senderId, data, ts) {
|
||||||
|
|
||||||
Wallet.prototype._onConnect = function(newCopayerId) {
|
Wallet.prototype._onConnect = function(newCopayerId) {
|
||||||
if (newCopayerId) {
|
if (newCopayerId) {
|
||||||
this.log('#### Setting new COPAYER:', newCopayerId);
|
log.debug('#### Setting new COPAYER:', newCopayerId);
|
||||||
this.sendWalletId(newCopayerId);
|
this.sendWalletId(newCopayerId);
|
||||||
}
|
}
|
||||||
var peerID = this.network.peerFromCopayer(newCopayerId)
|
var peerID = this.network.peerFromCopayer(newCopayerId)
|
||||||
|
|
@ -460,28 +455,12 @@ Wallet.prototype.netStart = function(callback) {
|
||||||
self.emit('ready', net.getPeer());
|
self.emit('ready', net.getPeer());
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
self.emit('publicKeyRingUpdated', true);
|
self.emit('publicKeyRingUpdated', true);
|
||||||
//self.scheduleConnect();
|
|
||||||
// no connection logic for now
|
// no connection logic for now
|
||||||
self.emit('txProposalsUpdated');
|
self.emit('txProposalsUpdated');
|
||||||
}, 10);
|
}, 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() {
|
Wallet.prototype.getRegisteredCopayerIds = function() {
|
||||||
var l = this.publicKeyRing.registeredCopayers();
|
var l = this.publicKeyRing.registeredCopayers();
|
||||||
var copayers = [];
|
var copayers = [];
|
||||||
|
|
@ -515,7 +494,7 @@ Wallet.prototype.keepAlive = function() {
|
||||||
try {
|
try {
|
||||||
this.lock.keepAlive();
|
this.lock.keepAlive();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.log(e);
|
log.debug(e);
|
||||||
this.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.');
|
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();
|
var wallet = this.toObj();
|
||||||
this.storage.setFromObj(this.id, wallet);
|
this.storage.setFromObj(this.id, wallet);
|
||||||
this.log('Wallet stored');
|
log.debug('Wallet stored');
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.toObj = function() {
|
Wallet.prototype.toObj = function() {
|
||||||
|
|
@ -613,7 +592,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients) {
|
||||||
Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
||||||
preconditions.checkArgument(ntxid);
|
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, {
|
this.send(recipients, {
|
||||||
type: 'txProposal',
|
type: 'txProposal',
|
||||||
txProposal: this.txProposals.get(ntxid).toObjTrim(),
|
txProposal: this.txProposals.get(ntxid).toObjTrim(),
|
||||||
|
|
@ -623,7 +602,7 @@ Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
|
||||||
|
|
||||||
Wallet.prototype.sendSeen = function(ntxid) {
|
Wallet.prototype.sendSeen = function(ntxid) {
|
||||||
preconditions.checkArgument(ntxid);
|
preconditions.checkArgument(ntxid);
|
||||||
this.log('### SENDING seen: ' + ntxid + ' TO: All');
|
log.debug('### SENDING seen: ' + ntxid + ' TO: All');
|
||||||
this.send(null, {
|
this.send(null, {
|
||||||
type: 'seen',
|
type: 'seen',
|
||||||
ntxid: ntxid,
|
ntxid: ntxid,
|
||||||
|
|
@ -633,7 +612,7 @@ Wallet.prototype.sendSeen = function(ntxid) {
|
||||||
|
|
||||||
Wallet.prototype.sendReject = function(ntxid) {
|
Wallet.prototype.sendReject = function(ntxid) {
|
||||||
preconditions.checkArgument(ntxid);
|
preconditions.checkArgument(ntxid);
|
||||||
this.log('### SENDING reject: ' + ntxid + ' TO: All');
|
log.debug('### SENDING reject: ' + ntxid + ' TO: All');
|
||||||
this.send(null, {
|
this.send(null, {
|
||||||
type: 'reject',
|
type: 'reject',
|
||||||
ntxid: ntxid,
|
ntxid: ntxid,
|
||||||
|
|
@ -643,7 +622,7 @@ Wallet.prototype.sendReject = function(ntxid) {
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.sendWalletReady = function(recipients) {
|
Wallet.prototype.sendWalletReady = function(recipients) {
|
||||||
this.log('### SENDING WalletReady TO:', recipients || 'All');
|
log.debug('### SENDING WalletReady TO:', recipients || 'All');
|
||||||
|
|
||||||
this.send(recipients, {
|
this.send(recipients, {
|
||||||
type: 'walletReady',
|
type: 'walletReady',
|
||||||
|
|
@ -652,7 +631,7 @@ Wallet.prototype.sendWalletReady = function(recipients) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendWalletId = 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, {
|
this.send(recipients, {
|
||||||
type: 'walletId',
|
type: 'walletId',
|
||||||
|
|
@ -664,7 +643,7 @@ Wallet.prototype.sendWalletId = function(recipients) {
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.sendPublicKeyRing = 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();
|
var publicKeyRing = this.publicKeyRing.toObj();
|
||||||
|
|
||||||
this.send(recipients, {
|
this.send(recipients, {
|
||||||
|
|
@ -675,7 +654,7 @@ Wallet.prototype.sendPublicKeyRing = function(recipients) {
|
||||||
};
|
};
|
||||||
Wallet.prototype.sendIndexes = function(recipients) {
|
Wallet.prototype.sendIndexes = function(recipients) {
|
||||||
var indexes = HDParams.serialize(this.publicKeyRing.indexes);
|
var indexes = HDParams.serialize(this.publicKeyRing.indexes);
|
||||||
this.log('### INDEXES TO:', recipients || 'All', indexes);
|
log.debug('### INDEXES TO:', recipients || 'All', indexes);
|
||||||
|
|
||||||
this.send(recipients, {
|
this.send(recipients, {
|
||||||
type: 'indexes',
|
type: 'indexes',
|
||||||
|
|
@ -685,7 +664,7 @@ Wallet.prototype.sendIndexes = function(recipients) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Wallet.prototype.sendAddressBook = 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, {
|
this.send(recipients, {
|
||||||
type: 'addressbook',
|
type: 'addressbook',
|
||||||
addressBook: this.addressBook,
|
addressBook: this.addressBook,
|
||||||
|
|
@ -796,23 +775,23 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
|
||||||
var tx = txp.builder.build();
|
var tx = txp.builder.build();
|
||||||
if (!tx.isComplete())
|
if (!tx.isComplete())
|
||||||
throw new Error('Tx is not complete. Can not broadcast');
|
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 scriptSig = tx.ins[0].getScript();
|
||||||
var size = scriptSig.serialize().length;
|
var size = scriptSig.serialize().length;
|
||||||
|
|
||||||
var txHex = tx.serialize().toString('hex');
|
var txHex = tx.serialize().toString('hex');
|
||||||
this.log('Raw transaction: ', txHex);
|
log.debug('Raw transaction: ', txHex);
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.blockchain.broadcast(txHex, function(err, txid) {
|
this.blockchain.broadcast(txHex, function(err, txid) {
|
||||||
self.log('BITCOIND txid:', txid);
|
log.debug('BITCOIND txid:', txid);
|
||||||
if (txid) {
|
if (txid) {
|
||||||
self.txProposals.get(ntxid).setSent(txid);
|
self.txProposals.get(ntxid).setSent(txid);
|
||||||
self.sendTxProposal(ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
return cb(txid);
|
return cb(txid);
|
||||||
} else {
|
} 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) {
|
self._checkSentTx(ntxid, function(txid) {
|
||||||
if (txid)
|
if (txid)
|
||||||
self.store();
|
self.store();
|
||||||
|
|
@ -1003,10 +982,8 @@ Wallet.prototype.receivePaymentRequest = function(options, pr, cb) {
|
||||||
self.emit('txProposalsUpdated');
|
self.emit('txProposalsUpdated');
|
||||||
}
|
}
|
||||||
|
|
||||||
self.log('You are currently on this BTC network:');
|
log.debug('You are currently on this BTC network:', network);
|
||||||
self.log(network);
|
log.debug('The server sent you a message:', memo);
|
||||||
self.log('The server sent you a message:');
|
|
||||||
self.log(memo);
|
|
||||||
|
|
||||||
return cb(ntxid, merchantData);
|
return cb(ntxid, merchantData);
|
||||||
});
|
});
|
||||||
|
|
@ -1025,7 +1002,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
||||||
|
|
||||||
var tx = txp.builder.build();
|
var tx = txp.builder.build();
|
||||||
if (!tx.isComplete()) return;
|
if (!tx.isComplete()) return;
|
||||||
this.log('Sending Transaction');
|
log.debug('Sending Transaction');
|
||||||
|
|
||||||
var refund_outputs = [];
|
var refund_outputs = [];
|
||||||
|
|
||||||
|
|
@ -1085,8 +1062,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) {
|
||||||
|
|
||||||
pay = pay.serialize();
|
pay = pay.serialize();
|
||||||
|
|
||||||
this.log('Sending Payment Message:');
|
log.debug('Sending Payment Message:', pay.toString('hex'));
|
||||||
this.log(pay.toString('hex'));
|
|
||||||
|
|
||||||
var buf = new ArrayBuffer(pay.length);
|
var buf = new ArrayBuffer(pay.length);
|
||||||
var view = new Uint8Array(buf);
|
var view = new Uint8Array(buf);
|
||||||
|
|
@ -1127,8 +1103,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
||||||
var payment = ack.get('payment');
|
var payment = ack.get('payment');
|
||||||
var memo = ack.get('memo');
|
var memo = ack.get('memo');
|
||||||
|
|
||||||
this.log('Our payment was acknowledged!');
|
log.debug('Our payment was acknowledged!');
|
||||||
this.log('Message from Merchant: %s', memo);
|
log.debug('Message from Merchant: %s', memo);
|
||||||
|
|
||||||
payment = PayPro.Payment.decode(payment);
|
payment = PayPro.Payment.decode(payment);
|
||||||
var pay = new PayPro();
|
var pay = new PayPro();
|
||||||
|
|
@ -1141,7 +1117,7 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
||||||
var tx = payment.message.transactions[0];
|
var tx = payment.message.transactions[0];
|
||||||
|
|
||||||
if (!tx) {
|
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) {
|
return this._checkSentTx(ntxid, function(txid) {
|
||||||
self.log('[Wallet.js.1048:txid:%s]', txid);
|
self.log('[Wallet.js.1048:txid:%s]', txid);
|
||||||
if (txid) self.store();
|
if (txid) self.store();
|
||||||
|
|
@ -1159,8 +1135,8 @@ Wallet.prototype.receivePaymentRequestACK = function(ntxid, tx, txp, ack, cb) {
|
||||||
|
|
||||||
var txid = tx.getHash().toString('hex');
|
var txid = tx.getHash().toString('hex');
|
||||||
var txHex = tx.serialize().toString('hex');
|
var txHex = tx.serialize().toString('hex');
|
||||||
this.log('Raw transaction: ', txHex);
|
log.debug('Raw transaction: ', txHex);
|
||||||
this.log('BITCOIND txid:', txid);
|
log.debug('BITCOIND txid:', txid);
|
||||||
this.txProposals.get(ntxid).setSent(txid);
|
this.txProposals.get(ntxid).setSent(txid);
|
||||||
this.sendTxProposal(ntxid);
|
this.sendTxProposal(ntxid);
|
||||||
this.store();
|
this.store();
|
||||||
|
|
@ -1260,10 +1236,7 @@ Wallet.prototype.createPaymentTxSync = function(options, merchantData, unspent)
|
||||||
|
|
||||||
if (options.fetch) return;
|
if (options.fetch) return;
|
||||||
|
|
||||||
this.log('');
|
log.debug('Created transaction: %s', b.tx.getStandardizedObject());
|
||||||
this.log('Created transaction:');
|
|
||||||
this.log(b.tx.getStandardizedObject());
|
|
||||||
this.log('');
|
|
||||||
|
|
||||||
var myId = this.getMyCopayerId();
|
var myId = this.getMyCopayerId();
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
@ -1661,7 +1634,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
|
|
||||||
Wallet.prototype.updateIndexes = function(callback) {
|
Wallet.prototype.updateIndexes = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.log('Updating indexes...');
|
log.debug('Updating indexes...');
|
||||||
|
|
||||||
var tasks = this.publicKeyRing.indexes.map(function(index) {
|
var tasks = this.publicKeyRing.indexes.map(function(index) {
|
||||||
return function(callback) {
|
return function(callback) {
|
||||||
|
|
@ -1671,7 +1644,7 @@ Wallet.prototype.updateIndexes = function(callback) {
|
||||||
|
|
||||||
async.parallel(tasks, function(err) {
|
async.parallel(tasks, function(err) {
|
||||||
if (err) callback(err);
|
if (err) callback(err);
|
||||||
self.log('Indexes updated');
|
log.debug('Indexes updated');
|
||||||
self.emit('publicKeyRingUpdated');
|
self.emit('publicKeyRingUpdated');
|
||||||
self.store();
|
self.store();
|
||||||
callback();
|
callback();
|
||||||
|
|
@ -1747,7 +1720,7 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb)
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.close = function() {
|
Wallet.prototype.close = function() {
|
||||||
this.log('## CLOSING');
|
log.debug('## CLOSING');
|
||||||
this.lock.release();
|
this.lock.release();
|
||||||
this.network.cleanUp();
|
this.network.cleanUp();
|
||||||
this.blockchain.destroy();
|
this.blockchain.destroy();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ var TxProposals = require('./TxProposals');
|
||||||
var PublicKeyRing = require('./PublicKeyRing');
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
var PrivateKey = require('./PrivateKey');
|
var PrivateKey = require('./PrivateKey');
|
||||||
var Wallet = require('./Wallet');
|
var Wallet = require('./Wallet');
|
||||||
|
var preconditions = require('preconditions').instance();
|
||||||
|
|
||||||
|
var log = require('../../log');
|
||||||
|
|
||||||
var Async = module.exports.Async = require('../network/Async');
|
var Async = module.exports.Async = require('../network/Async');
|
||||||
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
||||||
|
|
@ -11,7 +14,6 @@ var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../s
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* WalletFactory
|
* WalletFactory
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function WalletFactory(config, version) {
|
function WalletFactory(config, version) {
|
||||||
|
|
@ -27,19 +29,10 @@ function WalletFactory(config, version) {
|
||||||
this.blockchain = new this.Blockchain(config.blockchain);
|
this.blockchain = new this.Blockchain(config.blockchain);
|
||||||
|
|
||||||
this.networkName = config.networkName;
|
this.networkName = config.networkName;
|
||||||
this.verbose = config.verbose;
|
|
||||||
this.walletDefaults = config.wallet;
|
this.walletDefaults = config.wallet;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
WalletFactory.prototype.log = function() {
|
|
||||||
if (!this.verbose) return;
|
|
||||||
if (console) {
|
|
||||||
console.log.apply(console, arguments);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
WalletFactory.prototype._checkRead = function(walletId) {
|
WalletFactory.prototype._checkRead = function(walletId) {
|
||||||
var s = this.storage;
|
var s = this.storage;
|
||||||
var ret =
|
var ret =
|
||||||
|
|
@ -112,7 +105,7 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||||
|
|
||||||
WalletFactory.prototype.create = function(opts) {
|
WalletFactory.prototype.create = function(opts) {
|
||||||
opts = 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 = {
|
var privOpts = {
|
||||||
networkName: this.networkName,
|
networkName: this.networkName,
|
||||||
|
|
@ -137,12 +130,12 @@ WalletFactory.prototype.create = function(opts) {
|
||||||
opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(),
|
opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(),
|
||||||
opts.nickname
|
opts.nickname
|
||||||
);
|
);
|
||||||
this.log('\t### PublicKeyRing Initialized');
|
log.debug('\t### PublicKeyRing Initialized');
|
||||||
|
|
||||||
opts.txProposals = opts.txProposals || new TxProposals({
|
opts.txProposals = opts.txProposals || new TxProposals({
|
||||||
networkName: this.networkName,
|
networkName: this.networkName,
|
||||||
});
|
});
|
||||||
this.log('\t### TxProposals Initialized');
|
log.debug('\t### TxProposals Initialized');
|
||||||
|
|
||||||
this.storage._setPassphrase(opts.passphrase);
|
this.storage._setPassphrase(opts.passphrase);
|
||||||
|
|
||||||
|
|
@ -236,7 +229,7 @@ WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphras
|
||||||
|
|
||||||
//Create our PrivateK
|
//Create our PrivateK
|
||||||
var privateKey = new PrivateKey(privOpts);
|
var privateKey = new PrivateKey(privOpts);
|
||||||
this.log('\t### PrivateKey Initialized');
|
log.debug('\t### PrivateKey Initialized');
|
||||||
var opts = {
|
var opts = {
|
||||||
copayerId: privateKey.getId(),
|
copayerId: privateKey.getId(),
|
||||||
privkey: privateKey.getIdPriv(),
|
privkey: privateKey.getIdPriv(),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var log = require('../../log');
|
||||||
var AuthMessage = bitcore.AuthMessage;
|
var AuthMessage = bitcore.AuthMessage;
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var nodeUtil = require('util');
|
var nodeUtil = require('util');
|
||||||
|
|
@ -187,7 +188,7 @@ Network.prototype._onMessage = function(enc) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//console.log('receiving ' + JSON.stringify(payload));
|
log.debug('receiving ' + JSON.stringify(payload));
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
|
|
@ -234,8 +235,8 @@ Network.prototype._setupConnectionHandlers = function(cb) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Network.prototype._onError = function(err) {
|
Network.prototype._onError = function(err) {
|
||||||
console.log('RECV ERROR: ', err);
|
log.debug('RECV ERROR: ', err);
|
||||||
console.log(err.stack);
|
log.debug(err.stack);
|
||||||
this.criticalError = err.message;
|
this.criticalError = err.message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -348,7 +349,7 @@ Network.prototype.send = function(dest, payload, cb) {
|
||||||
var to = dest[ii];
|
var to = dest[ii];
|
||||||
if (to == this.copayerId)
|
if (to == this.copayerId)
|
||||||
continue;
|
continue;
|
||||||
//console.log('SEND to: ' + to, this.copayerId, payload);
|
log.debug('SEND to: ' + to, this.copayerId, payload);
|
||||||
var message = this.encode(to, payload);
|
var message = this.encode(to, payload);
|
||||||
this.socket.emit('message', message);
|
this.socket.emit('message', message);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
var CryptoJS = require('node-cryptojs-aes').CryptoJS;
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
var preconditions = require('preconditions').instance();
|
||||||
var id = 0;
|
var id = 0;
|
||||||
|
|
||||||
function Storage(opts) {
|
function Storage(opts) {
|
||||||
|
|
@ -11,15 +12,12 @@ function Storage(opts) {
|
||||||
if (opts.password)
|
if (opts.password)
|
||||||
this._setPassphrase(opts.password);
|
this._setPassphrase(opts.password);
|
||||||
|
|
||||||
try{
|
try {
|
||||||
this.localStorage = opts.localStorage || localStorage;
|
this.localStorage = opts.localStorage || localStorage;
|
||||||
this.sessionStorage = opts.sessionStorage || sessionStorage;
|
this.sessionStorage = opts.sessionStorage || sessionStorage;
|
||||||
} catch (e) {};
|
} catch (e) {}
|
||||||
|
preconditions.checkState(this.localStorage, 'No localstorage found');
|
||||||
if (!this.localStorage)
|
preconditions.checkState(this.sessionStorage, 'No sessionStorage found');
|
||||||
throw new Error('no localStorage');
|
|
||||||
if (!this.sessionStorage)
|
|
||||||
throw new Error('no sessionStorage');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var pps = {};
|
var pps = {};
|
||||||
|
|
@ -40,11 +38,6 @@ Storage.prototype._encrypt = function(string) {
|
||||||
return encryptedBase64;
|
return encryptedBase64;
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype._encryptObj = function(obj) {
|
|
||||||
var string = JSON.stringify(obj);
|
|
||||||
return this._encrypt(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
Storage.prototype._decrypt = function(base64) {
|
Storage.prototype._decrypt = function(base64) {
|
||||||
var decryptedStr = null;
|
var decryptedStr = null;
|
||||||
try {
|
try {
|
||||||
|
|
@ -58,10 +51,6 @@ Storage.prototype._decrypt = function(base64) {
|
||||||
return decryptedStr;
|
return decryptedStr;
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype._decryptObj = function(base64) {
|
|
||||||
var decryptedStr = this._decrypt(base64);
|
|
||||||
return JSON.parse(decryptedStr);
|
|
||||||
};
|
|
||||||
|
|
||||||
Storage.prototype._read = function(k) {
|
Storage.prototype._read = function(k) {
|
||||||
var ret;
|
var ret;
|
||||||
|
|
@ -98,7 +87,7 @@ Storage.prototype.removeGlobal = function(k) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype.getSessionId = function() {
|
Storage.prototype.getSessionId = function() {
|
||||||
var sessionId = this.sessionStorage.getItem('sessionId');
|
var sessionId = this.sessionStorage.getItem('sessionId');
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex');
|
sessionId = bitcore.SecureRandom.getRandomBuffer(8).toString('hex');
|
||||||
this.sessionStorage.setItem('sessionId', sessionId);
|
this.sessionStorage.setItem('sessionId', sessionId);
|
||||||
|
|
@ -131,7 +120,6 @@ Storage.prototype.setName = function(walletId, name) {
|
||||||
|
|
||||||
Storage.prototype.getName = function(walletId) {
|
Storage.prototype.getName = function(walletId) {
|
||||||
var ret = this.getGlobal('nameFor::' + walletId);
|
var ret = this.getGlobal('nameFor::' + walletId);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -145,7 +133,7 @@ Storage.prototype.getWalletIds = function() {
|
||||||
if (split.length == 2) {
|
if (split.length == 2) {
|
||||||
var walletId = split[0];
|
var walletId = split[0];
|
||||||
|
|
||||||
if (!walletId || walletId === 'nameFor' || walletId ==='lock')
|
if (!walletId || walletId === 'nameFor' || walletId === 'lock')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (typeof uniq[walletId] === 'undefined') {
|
if (typeof uniq[walletId] === 'undefined') {
|
||||||
|
|
@ -207,14 +195,14 @@ Storage.prototype.clearAll = function() {
|
||||||
this.localStorage.clear();
|
this.localStorage.clear();
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype.export = function(obj) {
|
Storage.prototype.import = function(base64) {
|
||||||
var encryptedObj = this._encryptObj(obj);
|
var decryptedStr = this._decrypt(base64);
|
||||||
return encryptedObj;
|
return JSON.parse(decryptedStr);
|
||||||
};
|
};
|
||||||
|
|
||||||
Storage.prototype.import = function(base64) {
|
Storage.prototype.export = function(obj) {
|
||||||
var decryptedObj = this._decryptObj(base64);
|
var string = JSON.stringify(obj);
|
||||||
return decryptedObj;
|
return this._encrypt(string);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = Storage;
|
module.exports = Storage;
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ angular
|
||||||
templateUrl: 'views/import.html',
|
templateUrl: 'views/import.html',
|
||||||
validate: false
|
validate: false
|
||||||
})
|
})
|
||||||
.when('/setup', {
|
.when('/create', {
|
||||||
templateUrl: 'views/setup.html',
|
templateUrl: 'views/create.html',
|
||||||
validate: false
|
validate: false
|
||||||
})
|
})
|
||||||
.when('/copayers', {
|
.when('/copayers', {
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,8 @@
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
|
|
||||||
angular.module('copayApp.services')
|
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 = {};
|
var root = {};
|
||||||
root.getVideoMutedStatus = function(copayer) {
|
|
||||||
if (!$rootScope.videoInfo) return;
|
|
||||||
|
|
||||||
var vi = $rootScope.videoInfo[copayer]
|
|
||||||
if (!vi) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return vi.muted;
|
|
||||||
};
|
|
||||||
|
|
||||||
root.redirIfLogged = function() {
|
root.redirIfLogged = function() {
|
||||||
if ($rootScope.wallet) {
|
if ($rootScope.wallet) {
|
||||||
|
|
@ -27,7 +18,6 @@ angular.module('copayApp.services')
|
||||||
$rootScope.wallet = null;
|
$rootScope.wallet = null;
|
||||||
delete $rootScope['wallet'];
|
delete $rootScope['wallet'];
|
||||||
|
|
||||||
video.close();
|
|
||||||
// Clear rootScope
|
// Clear rootScope
|
||||||
for (var i in $rootScope) {
|
for (var i in $rootScope) {
|
||||||
if (i.charAt(0) != '$') {
|
if (i.charAt(0) != '$') {
|
||||||
|
|
@ -102,18 +92,6 @@ angular.module('copayApp.services')
|
||||||
root.installStartupHandlers(w, $scope);
|
root.installStartupHandlers(w, $scope);
|
||||||
root.updateGlobalAddresses();
|
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
|
notification.enableHtml5Mode(); // for chrome: if support, enable it
|
||||||
|
|
||||||
w.on('corrupt', function(peerId) {
|
w.on('corrupt', function(peerId) {
|
||||||
|
|
@ -128,8 +106,6 @@ angular.module('copayApp.services')
|
||||||
} else {
|
} else {
|
||||||
$location.path('receive');
|
$location.path('receive');
|
||||||
}
|
}
|
||||||
if (!config.disableVideo)
|
|
||||||
video.setOwnPeer(myPeerID, w, handlePeerVideo);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
w.on('publicKeyRingUpdated', function(dontDigest) {
|
w.on('publicKeyRingUpdated', function(dontDigest) {
|
||||||
|
|
@ -172,9 +148,6 @@ angular.module('copayApp.services')
|
||||||
root.onErrorDigest(null, msg);
|
root.onErrorDigest(null, msg);
|
||||||
});
|
});
|
||||||
w.on('connect', function(peerID) {
|
w.on('connect', function(peerID) {
|
||||||
if (peerID && !config.disableVideo) {
|
|
||||||
video.callPeer(peerID, handlePeerVideo);
|
|
||||||
}
|
|
||||||
$rootScope.$digest();
|
$rootScope.$digest();
|
||||||
});
|
});
|
||||||
w.on('close', root.onErrorDigest);
|
w.on('close', root.onErrorDigest);
|
||||||
|
|
@ -217,7 +190,15 @@ angular.module('copayApp.services')
|
||||||
$rootScope.balanceByAddr = balanceByAddr;
|
$rootScope.balanceByAddr = balanceByAddr;
|
||||||
root.updateAddressList();
|
root.updateAddressList();
|
||||||
$rootScope.updatingBalance = false;
|
$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;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
83
js/services/rate.js
Normal file
83
js/services/rate.js
Normal file
|
|
@ -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);
|
||||||
6
js/services/request.js
Normal file
6
js/services/request.js
Normal file
|
|
@ -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-route/angular-route.min.js',
|
||||||
'lib/angular-foundation/mm-foundation.min.js',
|
'lib/angular-foundation/mm-foundation.min.js',
|
||||||
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
||||||
'lib/peerjs/peer.js',
|
|
||||||
'lib/bitcore.js',
|
'lib/bitcore.js',
|
||||||
'lib/crypto-js/rollups/sha256.js',
|
'lib/crypto-js/rollups/sha256.js',
|
||||||
'lib/crypto-js/rollups/pbkdf2.js',
|
'lib/crypto-js/rollups/pbkdf2.js',
|
||||||
|
|
@ -42,6 +41,7 @@ module.exports = function(config) {
|
||||||
|
|
||||||
//App-specific Code
|
//App-specific Code
|
||||||
'js/app.js',
|
'js/app.js',
|
||||||
|
'js/log.js',
|
||||||
'js/routes.js',
|
'js/routes.js',
|
||||||
'js/services/*.js',
|
'js/services/*.js',
|
||||||
'js/directives.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",
|
"travis-cov": "0.2.5",
|
||||||
"uglifyify": "1.2.3",
|
"uglifyify": "1.2.3",
|
||||||
"crypto-js": "3.1.2",
|
"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",
|
"main": "app.js",
|
||||||
"homepage": "https://github.com/bitpay/copay",
|
"homepage": "https://github.com/bitpay/copay",
|
||||||
|
|
|
||||||
0
test/run.sh
Normal file → Executable file
0
test/run.sh
Normal file → Executable file
|
|
@ -18,7 +18,7 @@ describe('Passphrase model', function() {
|
||||||
should.exist(p);
|
should.exist(p);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate key from password', function () {
|
it('should generate key from password', function (done) {
|
||||||
var p = new Passphrase({
|
var p = new Passphrase({
|
||||||
salt: 'mjuBtGybi/4=',
|
salt: 'mjuBtGybi/4=',
|
||||||
iterations: 10,
|
iterations: 10,
|
||||||
|
|
@ -33,6 +33,7 @@ describe('Passphrase model', function() {
|
||||||
|
|
||||||
p.getBase64Async(pass, function (ret) {
|
p.getBase64Async(pass, function (ret) {
|
||||||
ret.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
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,
|
totalCopayers: 5,
|
||||||
spendUnconfirmed: 1,
|
spendUnconfirmed: 1,
|
||||||
reconnectDelay: 100,
|
reconnectDelay: 100,
|
||||||
networkName: 'testnet'
|
networkName: 'testnet',
|
||||||
|
alternativeName: 'lol currency',
|
||||||
|
alternativeIsoCode: 'LOL'
|
||||||
};
|
};
|
||||||
|
|
||||||
it('Copay config should be binded', function() {
|
it('Copay config should be binded', function() {
|
||||||
|
|
@ -65,11 +67,11 @@ describe("Unit: Controllers", function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Setup Controller', function() {
|
describe('Create Controller', function() {
|
||||||
var setupCtrl;
|
var c;
|
||||||
beforeEach(inject(function($controller, $rootScope) {
|
beforeEach(inject(function($controller, $rootScope) {
|
||||||
scope = $rootScope.$new();
|
scope = $rootScope.$new();
|
||||||
setupCtrl = $controller('SetupController', {
|
c = $controller('CreateController', {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
@ -124,11 +126,21 @@ describe("Unit: Controllers", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Send Controller', function() {
|
describe('Send Controller', function() {
|
||||||
var scope, form, sendForm;
|
var scope, form, sendForm, sendCtrl;
|
||||||
beforeEach(angular.mock.module('copayApp'));
|
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 = $rootScope.$new();
|
||||||
|
scope.rateService = rateService;
|
||||||
$rootScope.wallet = new FakeWallet(walletConfig);
|
$rootScope.wallet = new FakeWallet(walletConfig);
|
||||||
|
config.alternativeName = 'lol currency';
|
||||||
|
config.alternativeIsoCode = 'LOL';
|
||||||
var element = angular.element(
|
var element = angular.element(
|
||||||
'<form name="form">' +
|
'<form name="form">' +
|
||||||
'<input type="text" id="newaddress" name="newaddress" ng-disabled="loading" placeholder="Address" ng-model="newaddress" valid-address required>' +
|
'<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">' +
|
'<form name="form2">' +
|
||||||
'<input type="text" id="address" name="address" ng-model="address" valid-address required>' +
|
'<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="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>' +
|
'<textarea id="comment" name="comment" ng-model="commentText" ng-maxlength="100"></textarea>' +
|
||||||
'</form>'
|
'</form>'
|
||||||
);
|
);
|
||||||
$compile(element2)(scope);
|
$compile(element2)(scope);
|
||||||
$controller('SendController', {
|
sendCtrl = $controller('SendController', {
|
||||||
$scope: scope,
|
$scope: scope,
|
||||||
$modal: {},
|
$modal: {},
|
||||||
});
|
});
|
||||||
|
|
@ -241,8 +254,22 @@ describe("Unit: Controllers", function() {
|
||||||
config.unitToSatoshi = old;
|
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() {
|
it('should create and send a transaction proposal', function() {
|
||||||
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
sendForm.address.$setViewValue('mkfTyEk7tfgV611Z4ESwDDSZwhsZdbMpVy');
|
||||||
|
|
|
||||||
|
|
@ -79,8 +79,6 @@ describe("Unit: controllerUtils", function() {
|
||||||
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
|
expect($rootScope.addrInfos[0].address).to.be.equal(Waddr);;
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Unit: Notification Service", function() {
|
describe("Unit: Notification Service", function() {
|
||||||
|
|
@ -118,12 +116,7 @@ describe("Unit: isMobile Service", function() {
|
||||||
isMobile.any().should.equal(true);
|
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() {
|
describe("Unit: uriHandler service", function() {
|
||||||
beforeEach(angular.mock.module('copayApp.services'));
|
beforeEach(angular.mock.module('copayApp.services'));
|
||||||
it('should contain a uriHandler service', inject(function(uriHandler) {
|
it('should contain a uriHandler service', inject(function(uriHandler) {
|
||||||
|
|
@ -135,3 +128,36 @@ describe("Unit: uriHandler service", function() {
|
||||||
}).should.not.throw();
|
}).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="box-setup-copayers p20">
|
||||||
<div class="oh">
|
<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()">
|
<div ng-if="!$root.wallet.publicKeyRing.isComplete()">
|
||||||
<img
|
<img
|
||||||
class="waiting br100 no-video"
|
class="waiting br100"
|
||||||
ng-if="!hasVideo(copayer)"
|
ng-if="!hasVideo(copayer)"
|
||||||
src="./img/satoshi.gif"
|
src="./img/satoshi.gif"
|
||||||
alt="Waiting Copayer"
|
alt="Waiting Copayer"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div ng-controller="SetupController">
|
<div ng-controller="CreateController">
|
||||||
<div data-alert class="loading-screen" ng-show="loading">
|
<div data-alert class="loading-screen" ng-show="loading">
|
||||||
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
|
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
|
||||||
Creating wallet...
|
Creating wallet...
|
||||||
|
|
@ -9,13 +9,13 @@
|
||||||
<a class="text-white" href="#!/open">Open a wallet</a>
|
<a class="text-white" href="#!/open">Open a wallet</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-setup" ng-show="!hasWallets">
|
<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>
|
||||||
<div class="button-setup">
|
<div class="button-setup">
|
||||||
<a class="text-primary" href="#!/join">Join a Wallet in Creation</a>
|
<a class="text-primary" href="#!/join">Join a Wallet in Creation</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="button-setup" ng-show="hasWallets">
|
<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>
|
||||||
|
|
||||||
<div class="footer-setup">
|
<div class="footer-setup">
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,9 @@
|
||||||
<div ng-controller="VideoController">
|
<div ng-controller="CopayersController">
|
||||||
<div class="video-box" ng-repeat="copayer in copayersList()">
|
<div class="copay-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>
|
|
||||||
|
|
||||||
<img
|
<img
|
||||||
class="br100 no-video"
|
class="br100 online"
|
||||||
ng-if="!hasVideo(copayer)"
|
|
||||||
ng-class="true || isConnected(copayer) ? 'online' : 'offline'"
|
|
||||||
src="./img/satoshi.gif"
|
src="./img/satoshi.gif"
|
||||||
alt="{{copayer}}"
|
alt="{{copayer.peerId}}"
|
||||||
width="70">
|
width="70">
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|
@ -1,55 +1,22 @@
|
||||||
<div ng-controller="VideoController" class="copayers">
|
<div ng-controller="CopayersController" class="copayers">
|
||||||
<div>
|
<h3>
|
||||||
<h3>
|
<i class="fi-torsos-all size-21 m20r"></i>
|
||||||
<i class="fi-torsos-all size-21 m20r"></i>
|
Copayers
|
||||||
Copayers
|
<small class="m15l">
|
||||||
<small class="m15l">
|
{{$root.wallet.requiredCopayers}} of {{$root.wallet.totalCopayers}}
|
||||||
{{$root.wallet.requiredCopayers}} of {{$root.wallet.totalCopayers}}
|
</small>
|
||||||
</small>
|
</h3>
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<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">
|
<div class="ellipsis" tooltip-placement="top" tooltip="{{copayer.nick}}">
|
||||||
<li class="ellipsis">
|
<small class="text-gray" ng-show="copayer.index == 0">Me</small>
|
||||||
<video
|
<small class="text-gray" ng-show="copayer.index > 0">{{copayer.nick}}</small>
|
||||||
ng-if="hasVideo(copayer)"
|
</div>
|
||||||
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,9 @@
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{totalBalanceBTC |noFractionNumber:8}} BTC"
|
tooltip="{{totalBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{totalBalance || 0
|
tooltip-placement="bottom">{{totalBalance || 0 |noFractionNumber}} {{$root.unitName}}
|
||||||
|noFractionNumber}} {{$root.unitName}}
|
|
||||||
</span>
|
</span>
|
||||||
<br>
|
<br>
|
||||||
Locked
|
Locked
|
||||||
|
|
@ -38,7 +37,7 @@
|
||||||
class="has-tip"
|
class="has-tip"
|
||||||
data-options="disable_for_touch:true"
|
data-options="disable_for_touch:true"
|
||||||
tooltip-popup-delay='500'
|
tooltip-popup-delay='500'
|
||||||
tooltip="{{lockedBalanceBTC |noFractionNumber:8}} BTC"
|
tooltip="{{lockedBalanceAlternative |noFractionNumber:2}} {{alternativeIsoCode}}"
|
||||||
tooltip-trigger="mouseenter"
|
tooltip-trigger="mouseenter"
|
||||||
tooltip-placement="bottom">{{lockedBalance || 0|noFractionNumber}} {{$root.unitName}}
|
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>
|
</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">
|
<div class="backup" ng-controller="MoreController">
|
||||||
<h1>Backup & Delete </h1>
|
<h1>Settings </h1>
|
||||||
<div class="oh large-12 columns panel">
|
<div class="oh large-12 columns panel">
|
||||||
<h3><i class="fi-download m10r"></i> Backup </h3>
|
<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">
|
<div class="large-4 columns">
|
||||||
<a class="button primary expand" ng-click="downloadBackup()">Download File</a>
|
<a class="button primary expand" ng-click="downloadBackup()">Download File</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
||||||
|
|
||||||
<div class="row" ng-show="wallet.isShared()">
|
<div class="row" ng-show="wallet.isShared()">
|
||||||
|
|
@ -110,7 +126,7 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</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">
|
<div class="send-note">
|
||||||
<h6>Send to</h6>
|
<h6>Send to</h6>
|
||||||
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
<p class="text-gray" ng-class="{'hidden': sendForm.address.$invalid || !address}">
|
||||||
|
|
@ -119,8 +135,11 @@
|
||||||
<h6>Total amount for this transaction:</h6>
|
<h6>Total amount for this transaction:</h6>
|
||||||
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
<p class="text-gray" ng-class="{'hidden': sendForm.amount.$invalid || !amount > 0}">
|
||||||
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
<b>{{amount + defaultFee |noFractionNumber}}</b> {{$root.unitName}}
|
||||||
|
<small ng-if="isRateAvailable">
|
||||||
|
{{ rateService.toFiat((amount + defaultFee) * unitToSatoshi, alternativeIsoCode) | noFractionNumber: 2 }} {{ alternativeIsoCode }}
|
||||||
|
<br>
|
||||||
|
</small>
|
||||||
<small>
|
<small>
|
||||||
{{ ((amount + defaultFee) * unitToBtc)|noFractionNumber:8}} BTC <br/>
|
|
||||||
Including fee of {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
Including fee of {{defaultFee|noFractionNumber}} {{$root.unitName}}
|
||||||
</small>
|
</small>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,9 @@
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Videoconferencing</legend>
|
<legend>Alternative Currency</legend>
|
||||||
<input id="disableVideo-opt" type="checkbox" ng-model="disableVideo" class="form-control">
|
<select class="form-control" ng-model="selectedAlternative" ng-options="alternative.name for alternative in alternativeOpts" required>
|
||||||
<label for="disableVideo-opt">Disable videoconferencing (for slow networks)</label>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Insight API server</legend>
|
<legend>Insight API server</legend>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue