bwc
This commit is contained in:
parent
04fb7ba032
commit
320de62f13
348 changed files with 7745 additions and 30874 deletions
66
src/js/controllers/backup.js
Normal file
66
src/js/controllers/backup.js
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('backupController',
|
||||
function($rootScope, $scope, $timeout, backupService, profileService, isMobile, isCordova, notification, go) {
|
||||
this.isSafari = isMobile.Safari();
|
||||
this.isCordova = isCordova;
|
||||
this.error = null;
|
||||
this.success = null;
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
this.isEncrypted = fc.isPrivKeyEncrypted();
|
||||
|
||||
this.copyText = function(text) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy(text);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
this.downloadWalletBackup = function() {
|
||||
backupService.walletDownload(this.password, function() {
|
||||
$rootScope.$emit('Local/BackupDone');
|
||||
notification.success('Backup created', 'Encrypted backup file saved');
|
||||
go.walletHome();
|
||||
});
|
||||
};
|
||||
|
||||
this.getBackup = function() {
|
||||
return backupService.walletExport(this.password);
|
||||
};
|
||||
|
||||
this.viewWalletBackup = function() {
|
||||
var self = this;
|
||||
this.loading = true;
|
||||
$timeout(function() {
|
||||
self.backupWalletPlainText = self.getBackup();
|
||||
$rootScope.$emit('Local/BackupDone');
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.copyWalletBackup = function() {
|
||||
var ew = this.getBackup();
|
||||
window.cordova.plugins.clipboard.copy(ew);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
$rootScope.$emit('Local/BackupDone');
|
||||
};
|
||||
|
||||
this.sendWalletBackup = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.toast.showShortCenter('Preparing backup...');
|
||||
var name = (fc.credentials.walletName || fc.credentials.walletId);
|
||||
var ew = this.getBackup();
|
||||
var properties = {
|
||||
subject: 'Copay Wallet Backup: ' + name,
|
||||
body: 'Here is the encrypted backup of the wallet ' + name + ': \n\n' + ew + '\n\n To import this backup, copy all text between {...}, including the symbols {}',
|
||||
isHtml: false
|
||||
};
|
||||
$rootScope.$emit('Local/BackupDone');
|
||||
window.plugin.email.open(properties);
|
||||
};
|
||||
|
||||
});
|
||||
102
src/js/controllers/copayers.js
Normal file
102
src/js/controllers/copayers.js
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('copayersController',
|
||||
function($scope, $rootScope, $timeout, $log, $modal, profileService, go, notification, isCordova) {
|
||||
var self = this;
|
||||
|
||||
|
||||
self.init = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
if (fc.isComplete()) {
|
||||
$log.debug('Wallet Complete...redirecting')
|
||||
go.walletHome();
|
||||
return;
|
||||
}
|
||||
self.loading = false;
|
||||
self.isCordova = isCordova;
|
||||
};
|
||||
|
||||
var _modalDeleteWallet = function() {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.title = 'Are you sure you want to delete this wallet?';
|
||||
$scope.loading = false;
|
||||
|
||||
$scope.ok = function() {
|
||||
$scope.loading = true;
|
||||
$modalInstance.close('ok');
|
||||
|
||||
};
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/confirmation.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl
|
||||
});
|
||||
modalInstance.result.then(function(ok) {
|
||||
if (ok) {
|
||||
_deleteWallet();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _deleteWallet = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
$timeout(function() {
|
||||
var fc = profileService.focusedClient;
|
||||
var walletName = fc.credentials.walletName;
|
||||
|
||||
profileService.deleteWalletFC({}, function(err) {
|
||||
if (err) {
|
||||
this.error = err.message || err;
|
||||
console.log(err);
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
});
|
||||
} else {
|
||||
go.walletHome();
|
||||
$timeout(function() {
|
||||
notification.success('Success', 'The wallet "' + walletName + '" was deleted');
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
self.deleteWallet = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
if (isCordova) {
|
||||
navigator.notification.confirm(
|
||||
'Are you sure you want to delete this wallet?',
|
||||
function(buttonIndex) {
|
||||
if (buttonIndex == 2) {
|
||||
_deleteWallet();
|
||||
}
|
||||
},
|
||||
'Confirm', ['Cancel', 'OK']
|
||||
);
|
||||
} else {
|
||||
_modalDeleteWallet();
|
||||
}
|
||||
};
|
||||
|
||||
self.copySecret = function(secret) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy(secret);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
self.shareSecret = function(secret) {
|
||||
if (isCordova) {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share(secret, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
75
src/js/controllers/create.js
Normal file
75
src/js/controllers/create.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('createController',
|
||||
function($scope, $rootScope, $location, $timeout, $log, lodash, go, profileService, configService, isCordova) {
|
||||
|
||||
var self = this;
|
||||
var defaults = configService.getDefaults();
|
||||
|
||||
/* For compressed keys, m*73 + n*34 <= 496 */
|
||||
var COPAYER_PAIR_LIMITS = {
|
||||
1: 1,
|
||||
2: 2,
|
||||
3: 3,
|
||||
4: 4,
|
||||
5: 4,
|
||||
6: 4,
|
||||
7: 3,
|
||||
8: 3,
|
||||
9: 2,
|
||||
10: 2,
|
||||
11: 1,
|
||||
12: 1,
|
||||
};
|
||||
|
||||
// ng-repeat defined number of times instead of repeating over array?
|
||||
this.getNumber = function(num) {
|
||||
return new Array(num);
|
||||
}
|
||||
|
||||
var updateRCSelect = function(n) {
|
||||
var maxReq = COPAYER_PAIR_LIMITS[n];
|
||||
self.RCValues = lodash.range(1, maxReq + 1);
|
||||
$scope.requiredCopayers = Math.min(parseInt(n / 2 + 1), maxReq);
|
||||
};
|
||||
|
||||
$scope.$watch('totalCopayers', function(tc) {
|
||||
updateRCSelect(tc);
|
||||
});
|
||||
|
||||
this.TCValues = lodash.range(1, defaults.limits.totalCopayers + 1);
|
||||
$scope.totalCopayers = defaults.wallet.totalCopayers;
|
||||
|
||||
this.create = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
this.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
var opts = {
|
||||
m: $scope.requiredCopayers,
|
||||
n: $scope.totalCopayers,
|
||||
name: form.walletName.$modelValue,
|
||||
extendedPrivateKey: form.privateKey.$modelValue,
|
||||
myName: $scope.totalCopayers > 1 ? form.myName.$modelValue : null,
|
||||
networkName: form.isTestnet.$modelValue ? 'testnet' : 'livenet'
|
||||
};
|
||||
self.loading = true;
|
||||
|
||||
$timeout(function() {
|
||||
profileService.createWallet(opts, function(err, secret) {
|
||||
self.loading = false;
|
||||
if (err) {
|
||||
$log.debug(err);
|
||||
self.error = 'Could not create wallet: ' + err;
|
||||
}
|
||||
else {
|
||||
go.walletHome();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.$on("$destroy", function() {
|
||||
$rootScope.hideWalletNavigation = false;
|
||||
});
|
||||
});
|
||||
29
src/js/controllers/createProfile.js
Normal file
29
src/js/controllers/createProfile.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('createProfileController', function($rootScope, $scope, $log, $timeout, profileService, go) {
|
||||
var self = this;
|
||||
|
||||
if (profileService.profile)
|
||||
go.walletHome();
|
||||
|
||||
var pin='';
|
||||
// $rootScope.$on('pin', function(event, pin) {
|
||||
self.creatingProfile = true;
|
||||
|
||||
$timeout(function() {
|
||||
profileService.create(pin, function(err) {
|
||||
if (err) {
|
||||
self.creatingProfile = false;
|
||||
$log.warn(err);
|
||||
self.error = err;
|
||||
$scope.$apply();
|
||||
$timeout(function() {
|
||||
go.reload();
|
||||
}, 3000);
|
||||
} else {
|
||||
go.walletHome();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
// });
|
||||
});
|
||||
16
src/js/controllers/devLogin.js
Normal file
16
src/js/controllers/devLogin.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('DevLoginController', function($scope, $rootScope, $routeParams, identityService) {
|
||||
|
||||
var mail = $routeParams.mail;
|
||||
var password = $routeParams.password;
|
||||
|
||||
var form = {};
|
||||
form.email = {};
|
||||
form.password = {};
|
||||
form.email.$modelValue = mail;
|
||||
form.password.$modelValue = password;
|
||||
|
||||
identityService.open($scope, form);
|
||||
|
||||
});
|
||||
6
src/js/controllers/emailConfirmation.js
Normal file
6
src/js/controllers/emailConfirmation.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('EmailConfirmationController', function($scope, $rootScope, $location) {
|
||||
$rootScope.fromEmailConfirmation = true;
|
||||
$location.path('/');
|
||||
});
|
||||
85
src/js/controllers/history.js
Normal file
85
src/js/controllers/history.js
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('historyController',
|
||||
function($scope, $rootScope, $filter, $timeout, $modal, $log, profileService, notification, go, configService, rateService, lodash) {
|
||||
|
||||
function strip(number) {
|
||||
return (parseFloat(number.toPrecision(12)));
|
||||
}
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
var config = configService.getSync().wallet.settings;
|
||||
var formatAmount = profileService.formatAmount;
|
||||
this.unitToSatoshi = config.unitToSatoshi;
|
||||
this.satToUnit = 1 / this.unitToSatoshi;
|
||||
this.unitName = config.unitName;
|
||||
this.alternativeIsoCode = config.alternativeIsoCode;
|
||||
|
||||
this.getUnitName = function() {
|
||||
return this.unitName;
|
||||
};
|
||||
|
||||
this.getAlternativeIsoCode = function() {
|
||||
return this.alternativeIsoCode;
|
||||
};
|
||||
|
||||
this._addRates = function(txs, cb) {
|
||||
if (!txs || txs.length == 0) return cb();
|
||||
var index = lodash.groupBy(txs, 'rateTs');
|
||||
|
||||
rateService.getHistoricRates(config.alternativeIsoCode, lodash.keys(index), function(err, res) {
|
||||
if (err || !res) return cb(err);
|
||||
lodash.each(res, function(r) {
|
||||
lodash.each(index[r.ts], function(tx) {
|
||||
var alternativeAmount = (r.rate != null ? tx.amount * rateService.SAT_TO_BTC * r.rate : null);
|
||||
tx.alternativeAmount = alternativeAmount ? $filter('noFractionNumber')(alternativeAmount, 2) : null;
|
||||
});
|
||||
});
|
||||
return cb();
|
||||
});
|
||||
};
|
||||
|
||||
this.openTxModal = function(btx) {
|
||||
var self = this;
|
||||
var fc = profileService.focusedClient;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance, profileService) {
|
||||
$scope.btx = btx;
|
||||
$scope.settings = config;
|
||||
$scope.btx.amountStr = profileService.formatAmount(btx.amount);
|
||||
$scope.color = fc.backgroundColor;
|
||||
|
||||
$scope.getAmount = function(amount) {
|
||||
return self.getAmount(amount);
|
||||
};
|
||||
|
||||
$scope.getUnitName = function() {
|
||||
return self.getUnitName();
|
||||
};
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
var n = fc.credentials.network;
|
||||
return n.substring(0, 4);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/tx-details.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
this.formatAmount = function(amount) {
|
||||
return profileService.formatAmount(amount);
|
||||
};
|
||||
|
||||
this.hasAction = function(actions, action) {
|
||||
return actions.hasOwnProperty('create');
|
||||
};
|
||||
|
||||
});
|
||||
81
src/js/controllers/import.js
Normal file
81
src/js/controllers/import.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('importController',
|
||||
function($scope, $rootScope, $location, $timeout, $log, profileService, notification, go, isMobile, isCordova, sjcl) {
|
||||
|
||||
var self = this;
|
||||
|
||||
this.isSafari = isMobile.Safari();
|
||||
this.isCordova = isCordova;
|
||||
var reader = new FileReader();
|
||||
|
||||
window.ignoreMobilePause = true;
|
||||
$scope.$on('$destroy', function() {
|
||||
$timeout(function(){
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
var _import = function(str, opts) {
|
||||
var str2;
|
||||
try {
|
||||
str2 = sjcl.decrypt(self.password, str);
|
||||
} catch (e) {
|
||||
self.error = 'Could not decrypt file, check your password';
|
||||
$log.warn(e);
|
||||
$scope.$apply();
|
||||
return;
|
||||
};
|
||||
|
||||
self.loading = true;
|
||||
|
||||
$timeout(function() {
|
||||
profileService.importWallet(str2, {
|
||||
compressed: null,
|
||||
password: null
|
||||
}, function(err, walletId) {
|
||||
self.loading = false;
|
||||
if (err) {
|
||||
self.error = err;
|
||||
}
|
||||
else {
|
||||
go.walletHome();
|
||||
notification.success('Success', 'Your wallet has been imported correctly');
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.getFile = function() {
|
||||
// If we use onloadend, we need to check the readyState.
|
||||
reader.onloadend = function(evt) {
|
||||
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
||||
_import(evt.target.result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.import = function(form) {
|
||||
if (form.$invalid) {
|
||||
this.error = 'There is an error in the form';
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
var backupFile = $scope.file;
|
||||
var backupText = form.backupText.$modelValue;
|
||||
var password = form.password.$modelValue;
|
||||
|
||||
if (!backupFile && !backupText) {
|
||||
this.error = 'Please, select your backup file';
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
|
||||
if (backupFile) {
|
||||
reader.readAsBinaryString(backupFile);
|
||||
} else {
|
||||
_import(backupText);
|
||||
}
|
||||
};
|
||||
});
|
||||
63
src/js/controllers/importLegacy.js
Normal file
63
src/js/controllers/importLegacy.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('importLegacyController',
|
||||
function($rootScope, $scope, $log, $timeout, notification, legacyImportService, profileService, go, lodash, bitcore) {
|
||||
|
||||
var self = this;
|
||||
self.messages = [];
|
||||
self.fromCloud = true;
|
||||
self.server = "https://insight.bitpay.com:443/api/email";
|
||||
|
||||
|
||||
$rootScope.$on('Local/ImportStatusUpdate', function(event, status) {
|
||||
$timeout(function() {
|
||||
$log.debug(status);
|
||||
|
||||
self.messages.unshift({
|
||||
message: status,
|
||||
});
|
||||
|
||||
var op = 1;
|
||||
lodash.each(self.messages, function(m) {
|
||||
if (op < 0.1) op = 0.1;
|
||||
m.opacity = op;
|
||||
op = op - 0.15;
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
|
||||
self.scan = function(ids) {
|
||||
$log.debug('### Scaning: ' + ids)
|
||||
var i = 0;
|
||||
lodash.each(ids, function(id) {
|
||||
$rootScope.$emit('Local/WalletImported', id);
|
||||
if (++i == ids.length) {
|
||||
go.walletHome();
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
self.import = function(form) {
|
||||
var username = form.username.$modelValue;
|
||||
var password = form.password.$modelValue;
|
||||
var serverURL = form.server.$modelValue;
|
||||
var fromCloud = form.fromCloud.$modelValue;
|
||||
|
||||
self.error = null;
|
||||
self.importing = true;
|
||||
$timeout(function() {
|
||||
legacyImportService.import(username, password, serverURL, fromCloud, function(err, ids, toScanIds) {
|
||||
if (err || !ids || !ids.length) {
|
||||
self.importing = false;
|
||||
self.error = err || 'Failed to import wallets';
|
||||
return;
|
||||
}
|
||||
|
||||
notification.success( ids.length + ' wallets imported. Funds scanning in progress. Hold on to see updated balance.');
|
||||
self.scan(toScanIds);
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
// TODO destroy event...
|
||||
});
|
||||
81
src/js/controllers/importProfile.js
Normal file
81
src/js/controllers/importProfile.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('importProfileController',
|
||||
function($scope, $rootScope, $timeout, notification, isMobile, isCordova, identityService) {
|
||||
this.importStatus = 'Importing profile - Reading backup...';
|
||||
this.hideAdv = true;
|
||||
this.isSafari = isMobile.Safari();
|
||||
this.isCordova = isCordova;
|
||||
|
||||
window.ignoreMobilePause = true;
|
||||
$scope.$on('$destroy', function() {
|
||||
$timeout(function(){
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
var reader = new FileReader();
|
||||
|
||||
var updateStatus = function(status) {
|
||||
this.importStatus = status;
|
||||
}
|
||||
|
||||
var _importBackup = function(str) {
|
||||
var password = this.password;
|
||||
updateStatus('Importing profile - Setting things up...');
|
||||
|
||||
identityService.importProfile(str,password, function(err, iden) {
|
||||
if (err) {
|
||||
$rootScope.starting = false;
|
||||
copay.logger.warn(err);
|
||||
if ((err.toString() || '').match('BADSTR')) {
|
||||
this.error = 'Bad password or corrupt profile file';
|
||||
} else if ((err.toString() || '').match('EEXISTS')) {
|
||||
this.error = 'Profile already exists';
|
||||
} else {
|
||||
this.error = 'Unknown error';
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.getFile = function() {
|
||||
// If we use onloadend, we need to check the readyState.
|
||||
reader.onloadend = function(evt) {
|
||||
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
|
||||
var encryptedObj = evt.target.result;
|
||||
_importBackup(encryptedObj);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
this.import = function(form) {
|
||||
|
||||
if (form.$invalid) {
|
||||
this.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
var backupFile = this.file;
|
||||
var backupText = form.backupText.$modelValue;
|
||||
var password = form.password.$modelValue;
|
||||
|
||||
if (!backupFile && !backupText) {
|
||||
this.error = 'Please, select your backup file';
|
||||
return;
|
||||
}
|
||||
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
|
||||
if (backupFile) {
|
||||
reader.readAsBinaryString(backupFile);
|
||||
} else {
|
||||
_importBackup(backupText);
|
||||
}
|
||||
}, 100);
|
||||
};
|
||||
});
|
||||
517
src/js/controllers/index.js
Normal file
517
src/js/controllers/index.js
Normal file
|
|
@ -0,0 +1,517 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, lodash, go, profileService, configService, isCordova, rateService, storageService) {
|
||||
var self = this;
|
||||
self.isCordova = isCordova;
|
||||
self.onGoingProcess = {};
|
||||
self.limitHistory = 5;
|
||||
|
||||
function strip(number) {
|
||||
return (parseFloat(number.toPrecision(12)));
|
||||
};
|
||||
|
||||
self.setOngoingProcess = function(processName, isOn) {
|
||||
$log.debug('onGoingProcess', processName, isOn);
|
||||
self[processName] = isOn;
|
||||
self.onGoingProcess[processName] = isOn;
|
||||
|
||||
// derived rules
|
||||
self.hideBalance = self.updatingBalance || self.updatingStatus || self.openingWallet;
|
||||
|
||||
var name;
|
||||
self.anyOnGoingProcess = lodash.any(self.onGoingProcess, function(isOn, processName) {
|
||||
if (isOn)
|
||||
name = name || processName;
|
||||
return isOn;
|
||||
});
|
||||
// The first one
|
||||
self.onGoingProcessName = name;
|
||||
};
|
||||
|
||||
self.setFocusedWallet = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
if (!fc) return;
|
||||
|
||||
$timeout(function() {
|
||||
self.hasProfile = true;
|
||||
self.noFocusedWallet = false;
|
||||
self.onGoingProcess = {};
|
||||
|
||||
// Credentials Shortcuts
|
||||
self.m = fc.credentials.m;
|
||||
self.n = fc.credentials.n;
|
||||
self.network = fc.credentials.network;
|
||||
self.copayerId = fc.credentials.copayerId;
|
||||
self.copayerName = fc.credentials.copayerName;
|
||||
self.requiresMultipleSignatures = fc.credentials.m > 1;
|
||||
self.isShared = fc.credentials.n > 1;
|
||||
self.walletName = fc.credentials.walletName;
|
||||
self.walletId = fc.credentials.walletId;
|
||||
self.isComplete = fc.isComplete();
|
||||
self.txps = [];
|
||||
self.copayers = [];
|
||||
self.setOngoingProcess('scanning', fc.scanning);
|
||||
self.lockedBalance = null;
|
||||
self.totalBalanceStr = null;
|
||||
self.notAuthorized = false;
|
||||
self.clientError = null;
|
||||
self.txHistory = [];
|
||||
self.txHistoryPaging = false;
|
||||
|
||||
storageService.getBackupFlag(self.walletId, function(err, val) {
|
||||
self.needsBackup = !val;
|
||||
self.openWallet();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.updateAll = function(walletStatus) {
|
||||
var get = function(cb) {
|
||||
if (walletStatus)
|
||||
return cb(null, walletStatus);
|
||||
else
|
||||
return fc.getStatus(cb);
|
||||
};
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
if (!fc) return;
|
||||
|
||||
$timeout(function() {
|
||||
self.setOngoingProcess('updatingStatus', true);
|
||||
$log.debug('Updating Status:', fc);
|
||||
get(function(err, walletStatus) {
|
||||
self.setOngoingProcess('updatingStatus', false);
|
||||
if (err) {
|
||||
self.handleError(err);
|
||||
return;
|
||||
}
|
||||
$log.debug('Wallet Status:', walletStatus);
|
||||
self.setPendingTxps(walletStatus.pendingTxps);
|
||||
|
||||
// Status Shortcuts
|
||||
self.walletName = walletStatus.wallet.name;
|
||||
self.walletSecret = walletStatus.wallet.secret;
|
||||
self.walletStatus = walletStatus.wallet.status;
|
||||
self.copayers = walletStatus.wallet.copayers;
|
||||
self.setBalance(walletStatus.balance);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.updateBalance = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
$timeout(function() {
|
||||
self.setOngoingProcess('updatingBalance', true);
|
||||
$log.debug('Updating Balance');
|
||||
fc.getBalance(function(err, balance) {
|
||||
self.setOngoingProcess('updatingBalance', false);
|
||||
if (err) {
|
||||
$log.debug('Wallet Balance ERROR:', err);
|
||||
$scope.$emit('Local/ClientError', err);
|
||||
return;
|
||||
}
|
||||
$log.debug('Wallet Balance:', balance);
|
||||
self.setBalance(balance);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.updatePendingTxps = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
$timeout(function() {
|
||||
self.setOngoingProcess('updatingPendingTxps', true);
|
||||
$log.debug('Updating PendingTxps');
|
||||
fc.getTxProposals({}, function(err, txps) {
|
||||
self.setOngoingProcess('updatingPendingTxps', false);
|
||||
if (err) {
|
||||
$log.debug('Wallet PendingTxps ERROR:', err);
|
||||
$scope.$emit('Local/ClientError', err);
|
||||
} else {
|
||||
$log.debug('Wallet PendingTxps:', txps);
|
||||
self.setPendingTxps(txps);
|
||||
}
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.updateTxHistory = function(skip) {
|
||||
var fc = profileService.focusedClient;
|
||||
if (!skip) {
|
||||
self.txHistory = [];
|
||||
}
|
||||
self.skipHistory = skip || 0;
|
||||
$timeout(function() {
|
||||
self.setOngoingProcess('updatingTxHistory', true);
|
||||
$log.debug('Updating Transaction History');
|
||||
fc.getTxHistory({
|
||||
skip: self.skipHistory,
|
||||
limit: self.limitHistory + 1
|
||||
}, function(err, txs) {
|
||||
self.setOngoingProcess('updatingTxHistory', false);
|
||||
if (err) {
|
||||
$log.debug('TxHistory ERROR:', err);
|
||||
$scope.$emit('Local/ClientError', err);
|
||||
}
|
||||
else {
|
||||
$log.debug('Wallet Transaction History:', txs);
|
||||
self.skipHistory = self.skipHistory + self.limitHistory;
|
||||
self.setTxHistory(txs);
|
||||
}
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.handleError = function(err) {
|
||||
$log.debug('ERROR:', err);
|
||||
if (err.code === 'NOTAUTHORIZED') {
|
||||
$scope.$emit('Local/NotAuthorized');
|
||||
} else if (err.code === 'NOTFOUND') {
|
||||
$scope.$emit('Local/BWSNotFound');
|
||||
} else if (err.code === 'ETIMEDOUT') {
|
||||
$scope.$emit('Local/BWSTimeOut');
|
||||
} else {
|
||||
$scope.$emit('Local/ClientError', err);
|
||||
}
|
||||
};
|
||||
self.openWallet = function() {
|
||||
var fc = profileService.focusedClient;
|
||||
self.updateColor();
|
||||
$timeout(function() {
|
||||
self.setOngoingProcess('openingWallet', true);
|
||||
fc.openWallet(function(err, walletStatus) {
|
||||
self.setOngoingProcess('openingWallet', false);
|
||||
if (err) {
|
||||
self.handleError(err);
|
||||
return;
|
||||
}
|
||||
$log.debug('Wallet Opened');
|
||||
self.updateAll(lodash.isObject(walletStatus) ? walletStatus : null);
|
||||
$rootScope.$apply();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self.setPendingTxps = function(txps) {
|
||||
var config = configService.getSync().wallet.settings;
|
||||
self.pendingTxProposalsCountForUs = 0;
|
||||
lodash.each(txps, function(tx) {
|
||||
var amount = tx.amount * self.satToUnit;
|
||||
tx.amountStr = profileService.formatAmount(tx.amount) + ' ' + config.unitName;
|
||||
tx.alternativeAmount = rateService.toFiat(tx.amount, config.alternativeIsoCode) ? rateService.toFiat(tx.amount, config.alternativeIsoCode).toFixed(2) : 'N/A';
|
||||
tx.alternativeAmountStr = tx.alternativeAmount + " " + config.alternativeIsoCode;
|
||||
tx.alternativeIsoCode = config.alternativeIsoCode;
|
||||
|
||||
|
||||
|
||||
var action = lodash.find(tx.actions, {
|
||||
copayerId: self.copayerId
|
||||
});
|
||||
|
||||
if (!action && tx.status == 'pending') {
|
||||
tx.pendingForUs = true;
|
||||
}
|
||||
|
||||
if (action && action.type == 'accept') {
|
||||
tx.statusForUs = 'accepted';
|
||||
} else if (action && action.type == 'reject') {
|
||||
tx.statusForUs = 'rejected';
|
||||
} else {
|
||||
tx.statusForUs = 'pending';
|
||||
}
|
||||
|
||||
if (tx.creatorId == self.copayerId && tx.actions.length == 1) {
|
||||
tx.couldRemove = true;
|
||||
};
|
||||
|
||||
if (tx.creatorId != self.copayerId) {
|
||||
self.pendingTxProposalsCountForUs = self.pendingTxProposalsCountForUs + 1;
|
||||
}
|
||||
});
|
||||
self.txps = txps;
|
||||
};
|
||||
|
||||
self.setTxHistory = function(txs) {
|
||||
var now = new Date();
|
||||
var c = 0;
|
||||
self.txHistoryPaging = txs[self.limitHistory] ? true : false;
|
||||
lodash.each(txs, function(tx) {
|
||||
tx.ts = tx.minedTs || tx.sentTs;
|
||||
tx.rateTs = Math.floor((tx.ts || now) / 1000);
|
||||
tx.amountStr = profileService.formatAmount(tx.amount); //$filter('noFractionNumber')(
|
||||
if (c < self.limitHistory) {
|
||||
self.txHistory.push(tx);
|
||||
c++;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.updateColor = function() {
|
||||
var config = configService.getSync();
|
||||
config.colorFor = config.colorFor || {};
|
||||
self.backgroundColor = config.colorFor[self.walletId] || '#1ABC9C';
|
||||
var fc = profileService.focusedClient;
|
||||
fc.backgroundColor = self.backgroundColor;
|
||||
};
|
||||
|
||||
self.setBalance = function(balance) {
|
||||
if (!balance) return;
|
||||
var config = configService.getSync().wallet.settings;
|
||||
var COIN = 1e8;
|
||||
|
||||
// Address with Balance
|
||||
self.balanceByAddress = balance.byAddress;
|
||||
|
||||
// SAT
|
||||
self.totalBalanceSat = balance.totalAmount;
|
||||
self.lockedBalanceSat = balance.lockedAmount;
|
||||
self.availableBalanceSat = self.totalBalanceSat - self.lockedBalanceSat;
|
||||
|
||||
// Selected unit
|
||||
self.unitToSatoshi = config.unitToSatoshi;
|
||||
self.satToUnit = 1 / self.unitToSatoshi;
|
||||
self.unitName = config.unitName;
|
||||
|
||||
self.totalBalance = strip(self.totalBalanceSat * self.satToUnit);
|
||||
self.lockedBalance = strip(self.lockedBalanceSat * self.satToUnit);
|
||||
self.availableBalance = strip(self.availableBalanceSat * self.satToUnit);
|
||||
|
||||
// BTC
|
||||
self.totalBalanceBTC = strip(self.totalBalanceSat / COIN);
|
||||
self.lockedBalanceBTC = strip(self.lockedBalanceSat / COIN);
|
||||
self.availableBalanceBTC = strip(self.availableBalanceBTC / COIN);
|
||||
|
||||
|
||||
//STR
|
||||
self.totalBalanceStr = profileService.formatAmount(self.totalBalanceSat) + ' ' + self.unitName;
|
||||
self.lockedBalanceStr = profileService.formatAmount(self.lockedBalanceSat) + ' ' + self.unitName;
|
||||
self.availableBalanceStr = profileService.formatAmount(self.availableBalanceSat) + ' ' + self.unitName;
|
||||
|
||||
self.alternativeName = config.alternativeName;
|
||||
self.alternativeIsoCode = config.alternativeIsoCode;
|
||||
|
||||
// Check address
|
||||
self.checkLastAddress(balance.byAddress);
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
|
||||
var totalBalanceAlternative = rateService.toFiat(self.totalBalance * self.unitToSatoshi, self.alternativeIsoCode);
|
||||
var lockedBalanceAlternative = rateService.toFiat(self.lockedBalance * self.unitToSatoshi, self.alternativeIsoCode);
|
||||
var alternativeConversionRate = rateService.toFiat(100000000, self.alternativeIsoCode);
|
||||
|
||||
self.totalBalanceAlternative = $filter('noFractionNumber')(totalBalanceAlternative, 2);
|
||||
self.lockedBalanceAlternative = $filter('noFractionNumber')(lockedBalanceAlternative, 2);
|
||||
self.alternativeConversionRate = $filter('noFractionNumber')(alternativeConversionRate, 2);
|
||||
|
||||
self.alternativeBalanceAvailable = true;
|
||||
|
||||
self.alternativeBalanceAvailable = true;
|
||||
self.updatingBalance = false;
|
||||
|
||||
self.isRateAvailable = true;
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
if (!rateService.isAvailable()) {
|
||||
$rootScope.$apply();
|
||||
}
|
||||
};
|
||||
|
||||
self.checkLastAddress = function(byAddress, cb) {
|
||||
storageService.getLastAddress(self.walletId, function(err, addr) {
|
||||
var used = lodash.find(byAddress, {
|
||||
address: addr
|
||||
});
|
||||
if (used) {
|
||||
$log.debug('Address ' + addr + ' was used. Cleaning Cache.')
|
||||
$rootScope.$emit('Local/NeedNewAddress', err);
|
||||
storageService.clearLastAddress(self.walletId, function(err, addr) {
|
||||
if (cb) return cb();
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
self.recreate = function(cb) {
|
||||
var fc = profileService.focusedClient;
|
||||
self.setOngoingProcess('recreating', true);
|
||||
fc.recreateWallet(function(err) {
|
||||
self.notAuthorized = false;
|
||||
self.setOngoingProcess('recreating', false);
|
||||
|
||||
profileService.setWalletClients();
|
||||
$timeout(function() {
|
||||
$rootScope.$emit('Local/WalletImported', self.walletId);
|
||||
}, 100);
|
||||
});
|
||||
};
|
||||
|
||||
self.openMenu = function() {
|
||||
go.swipe(true);
|
||||
};
|
||||
|
||||
self.closeMenu = function() {
|
||||
go.swipe();
|
||||
};
|
||||
|
||||
self.startScan = function(walletId) {
|
||||
var c = profileService.walletClients[walletId];
|
||||
c.scanning = true;
|
||||
|
||||
if (self.walletId == walletId)
|
||||
self.setOngoingProcess('scanning', true);
|
||||
|
||||
c.startScan({
|
||||
includeCopayerBranches: true,
|
||||
}, function(err) {
|
||||
if (err) {
|
||||
c.scanning = false;
|
||||
if (self.walletId == walletId)
|
||||
self.setOngoingProcess('scanning', false);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// UX event handlers
|
||||
$rootScope.$on('Local/ColorUpdated', function(event) {
|
||||
self.updateColor();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/ConfigurationUpdated', function(event) {
|
||||
self.updateAll();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/WalletCompleted', function(event) {
|
||||
self.setFocusedWallet();
|
||||
go.walletHome();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/OnLine', function(event) {
|
||||
self.isOffLine = false;
|
||||
self.updateAll();
|
||||
self.updateTxHistory();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/OffLine', function(event) {
|
||||
self.isOffLine = true;
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/BackupDone', function(event) {
|
||||
self.needsBackup = false;
|
||||
storageService.setBackupFlag(self.walletId, function() {});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/NotAuthorized', function(event) {
|
||||
self.notAuthorized = true;
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/BWSNotFound', function(event) {
|
||||
self.clientError = 'Could not access to Bitcore Wallet Service: Service not found';
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/BWSTimeOut', function(event) {
|
||||
self.clientError = 'Could not access to Bitcore Wallet Service: Timed out';
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/ClientError', function(event, err) {
|
||||
self.clientError = err;
|
||||
$rootScope.$apply();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/WalletImported', function(event, walletId) {
|
||||
self.startScan(walletId);
|
||||
});
|
||||
|
||||
$rootScope.$on('Animation/Disable', function(event) {
|
||||
$timeout(function() {
|
||||
self.swipeLeft = false;
|
||||
self.swipeRight = false;
|
||||
}, 350);
|
||||
});
|
||||
|
||||
$rootScope.$on('Animation/SwipeLeft', function(event) {
|
||||
self.swipeLeft = true;
|
||||
});
|
||||
|
||||
$rootScope.$on('Animation/SwipeRight', function(event) {
|
||||
self.swipeRight = true;
|
||||
});
|
||||
|
||||
lodash.each(['NewIncomingTx', 'ScanFinished'], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
if (eventName == 'ScanFinished') {
|
||||
self.setOngoingProcess('scanning', false);
|
||||
}
|
||||
self.updateBalance();
|
||||
$timeout(function() {
|
||||
self.updateTxHistory();
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
|
||||
// remove transactionProposalRemoved (only for compat)
|
||||
|
||||
lodash.each(['NewOutgoingTx', 'NewTxProposal', 'TxProposalFinallyRejected', 'transactionProposalRemoved', 'TxProposalRemoved',
|
||||
'Local/NewTxProposal', 'Local/TxProposalAction'
|
||||
], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
self.updateAll();
|
||||
$timeout(function() {
|
||||
self.updateTxHistory();
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy'], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
var f = function() {
|
||||
if (self.updatingStatus) {
|
||||
return $timeout(f, 200);
|
||||
};
|
||||
self.updatePendingTxps();
|
||||
};
|
||||
f();
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/NoWallets', function(event) {
|
||||
$timeout(function() {
|
||||
self.hasProfile = true;
|
||||
self.noFocusedWallet = true;
|
||||
self.clientError = null;
|
||||
self.isComplete = null;
|
||||
self.walletName = null;
|
||||
go.addWallet();
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/NewFocusedWallet', function() {
|
||||
self.setFocusedWallet();
|
||||
self.updateTxHistory();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/NeedsPassword', function(event, isSetup, cb) {
|
||||
self.askPassword = {
|
||||
isSetup: isSetup,
|
||||
callback: function (err, pass) {
|
||||
self.askPassword = null;
|
||||
return cb(err, pass);
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
lodash.each(['NewCopayer', 'CopayerUpdated'], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
// Re try to open wallet (will triggers)
|
||||
self.setFocusedWallet();
|
||||
});
|
||||
});
|
||||
});
|
||||
158
src/js/controllers/join.js
Normal file
158
src/js/controllers/join.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('joinController',
|
||||
function($scope, $rootScope, $timeout, go, isMobile, notification, profileService, isCordova, $modal) {
|
||||
|
||||
var self = this;
|
||||
|
||||
//TODO : make one function - this was copied from topbar.js
|
||||
var cordovaOpenScanner = function() {
|
||||
window.ignoreMobilePause = true;
|
||||
cordova.plugins.barcodeScanner.scan(
|
||||
function onSuccess(result) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
if (result.cancelled) return;
|
||||
|
||||
$timeout(function() {
|
||||
var data = result.text;
|
||||
$scope.secret = data;
|
||||
$scope.joinForm.secret.$setViewValue(data);
|
||||
$scope.joinForm.secret.$render();
|
||||
}, 1000);
|
||||
},
|
||||
function onError(error) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
alert('Scanning error');
|
||||
});
|
||||
};
|
||||
|
||||
var modalOpenScanner = function() {
|
||||
var _scope = $scope;
|
||||
var ModalInstanceCtrl = function($scope, $rootScope, $modalInstance) {
|
||||
// QR code Scanner
|
||||
var video;
|
||||
var canvas;
|
||||
var $video;
|
||||
var context;
|
||||
var localMediaStream;
|
||||
|
||||
var _scan = function(evt) {
|
||||
|
||||
if (localMediaStream) {
|
||||
context.drawImage(video, 0, 0, 300, 225);
|
||||
try {
|
||||
qrcode.decode();
|
||||
} catch (e) {
|
||||
//qrcodeError(e);
|
||||
}
|
||||
}
|
||||
$timeout(_scan, 500);
|
||||
};
|
||||
|
||||
var _scanStop = function() {
|
||||
if (localMediaStream && localMediaStream.stop) localMediaStream.stop();
|
||||
localMediaStream = null;
|
||||
video.src = '';
|
||||
};
|
||||
|
||||
qrcode.callback = function(data) {
|
||||
_scanStop();
|
||||
$modalInstance.close(data);
|
||||
};
|
||||
|
||||
var _successCallback = function(stream) {
|
||||
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
localMediaStream = stream;
|
||||
video.play();
|
||||
$timeout(_scan, 1000);
|
||||
};
|
||||
|
||||
var _videoError = function(err) {
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
var setScanner = function() {
|
||||
|
||||
navigator.getUserMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL ||
|
||||
window.mozURL || window.msURL;
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
setScanner();
|
||||
$timeout(function() {
|
||||
canvas = document.getElementById('qr-canvas');
|
||||
context = canvas.getContext('2d');
|
||||
|
||||
video = document.getElementById('qrcode-scanner-video');
|
||||
$video = angular.element(video);
|
||||
canvas.width = 300;
|
||||
canvas.height = 225;
|
||||
context.clearRect(0, 0, 300, 225);
|
||||
|
||||
navigator.getUserMedia({
|
||||
video: true
|
||||
}, _successCallback, _videoError);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
_scanStop();
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/scanner.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
modalInstance.result.then(function(data) {
|
||||
$scope.secret = data;
|
||||
$scope.joinForm.secret.$setViewValue(data);
|
||||
$scope.joinForm.secret.$render();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.openScanner = function() {
|
||||
if (isCordova) {
|
||||
cordovaOpenScanner();
|
||||
} else {
|
||||
modalOpenScanner();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.join = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
notification.error('Error', 'Please enter the required fields');
|
||||
return;
|
||||
}
|
||||
self.loading = true;
|
||||
|
||||
$timeout(function() {
|
||||
profileService.joinWallet({
|
||||
secret: form.secret.$modelValue,
|
||||
extendedPrivateKey: form.privateKey.$modelValue,
|
||||
myName: form.myName.$modelValue
|
||||
}, function(err) {
|
||||
self.loading = false;
|
||||
if (err) {
|
||||
notification.error(err);
|
||||
}
|
||||
else {
|
||||
go.walletHome();
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
27
src/js/controllers/menu.js
Normal file
27
src/js/controllers/menu.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('menuController', function($state) {
|
||||
|
||||
this.menu = [{
|
||||
'title': 'Home',
|
||||
'icon': 'icon-home',
|
||||
'link': 'walletHome'
|
||||
}, {
|
||||
'title': 'Receive',
|
||||
'icon': 'icon-receive',
|
||||
'link': 'receive'
|
||||
}, {
|
||||
'title': 'Send',
|
||||
'icon': 'icon-paperplane',
|
||||
'link': 'send'
|
||||
}, {
|
||||
'title': 'History',
|
||||
'icon': 'icon-history',
|
||||
'link': 'history'
|
||||
}];
|
||||
|
||||
this.go = function(state) {
|
||||
$state.go(state);
|
||||
};
|
||||
|
||||
});
|
||||
41
src/js/controllers/password.js
Normal file
41
src/js/controllers/password.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('passwordController',
|
||||
function($rootScope, $scope, $timeout, profileService, notification, go) {
|
||||
|
||||
var self = this;
|
||||
|
||||
var pass1;
|
||||
|
||||
self.isVerification = false;
|
||||
|
||||
self.close = function(cb){
|
||||
return cb('No password given');
|
||||
};
|
||||
|
||||
self.set = function(isSetup, cb){
|
||||
self.error = false;
|
||||
|
||||
if (isSetup && !self.isVerification) {
|
||||
self.isVerification = true;
|
||||
pass1= self.password;
|
||||
self.password = null;
|
||||
$timeout(function(){
|
||||
$rootScope.$apply();
|
||||
})
|
||||
return;
|
||||
}
|
||||
if (isSetup) {
|
||||
if (pass1 != self.password) {
|
||||
self.error = 'Passwords do not match';
|
||||
self.isVerification = false;
|
||||
self.password = null;
|
||||
pass1 =null;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return cb(null, self.password);
|
||||
};
|
||||
|
||||
});
|
||||
60
src/js/controllers/paymentUri.js
Normal file
60
src/js/controllers/paymentUri.js
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('paymentUriController',
|
||||
function($rootScope, $stateParams, $location, $timeout, profileService, configService, lodash, bitcore, go) {
|
||||
|
||||
function strip(number) {
|
||||
return (parseFloat(number.toPrecision(12)));
|
||||
};
|
||||
|
||||
// Build bitcoinURI with querystring
|
||||
this.checkBitcoinUri = function() {
|
||||
var query = [];
|
||||
angular.forEach($location.search(), function(value, key) {
|
||||
query.push(key + "=" + value);
|
||||
});
|
||||
var queryString = query ? query.join("&") : null;
|
||||
this.bitcoinURI = $stateParams.data + ( queryString ? '?' + queryString : '');
|
||||
|
||||
var URI = bitcore.URI;
|
||||
var isUriValid = URI.isValid(this.bitcoinURI);
|
||||
if (!URI.isValid(this.bitcoinURI)) {
|
||||
this.error = true;
|
||||
return;
|
||||
}
|
||||
var uri = new URI(this.bitcoinURI);
|
||||
|
||||
if (uri && uri.address) {
|
||||
var config = configService.getSync().wallet.settings;
|
||||
var unitToSatoshi = config.unitToSatoshi;
|
||||
var satToUnit = 1 / unitToSatoshi;
|
||||
var unitName = config.unitName;
|
||||
|
||||
uri.amount = strip(uri.amount * satToUnit) + ' ' + unitName;
|
||||
return uri;
|
||||
}
|
||||
};
|
||||
|
||||
this.getWallets = function() {
|
||||
if (!profileService.profile) return;
|
||||
var ret = lodash.map(profileService.profile.credentials, function(c) {
|
||||
return {
|
||||
m: c.m,
|
||||
n: c.n,
|
||||
name: c.walletName,
|
||||
id: c.walletId,
|
||||
};
|
||||
});
|
||||
return lodash.sortBy(ret, 'walletName');
|
||||
};
|
||||
|
||||
this.selectWallet = function(wid) {
|
||||
var self = this;
|
||||
if (wid != profileService.focusedClient.credentials.walletId) {
|
||||
profileService.setAndStoreFocus(wid, function() {});
|
||||
}
|
||||
go.send();
|
||||
$timeout(function() {
|
||||
$rootScope.$emit('paymentUri', self.bitcoinURI);
|
||||
}, 100);
|
||||
};
|
||||
});
|
||||
73
src/js/controllers/pinController.js
Normal file
73
src/js/controllers/pinController.js
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('pinController', function($scope, $timeout) {
|
||||
this.init = function(confirmPin, testPin) {
|
||||
this._firstpin = null;
|
||||
this.askForPin = 1;
|
||||
this.confirmPin = confirmPin;
|
||||
this.clear();
|
||||
if (testPin) {
|
||||
console.log('WARN: using test pin:', testPin);
|
||||
$timeout(function() {
|
||||
$scope.$emit('pin', testPin);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
this.clear = function() {
|
||||
this.digits = [];
|
||||
this.defined = [];
|
||||
};
|
||||
|
||||
this.press = function(digit) {
|
||||
var self = this;
|
||||
$timeout(function() {
|
||||
self._press(digit);
|
||||
}, 1);
|
||||
};
|
||||
|
||||
this._press = function(digit) {
|
||||
var self = this;
|
||||
this.error = null;
|
||||
this.digits.push(digit);
|
||||
this.defined.push(true);
|
||||
|
||||
if (this.digits.length == 4) {
|
||||
var pin = this.digits.join('');
|
||||
|
||||
if (this.confirmPin) {
|
||||
if (!this._firstpin) {
|
||||
this._firstpin = pin;
|
||||
this.askForPin = 2;
|
||||
$timeout(function() {
|
||||
self.clear();
|
||||
}, 100);
|
||||
return;
|
||||
} else {
|
||||
if (pin === this._firstpin) {
|
||||
$scope.$emit('pin', pin);
|
||||
return;
|
||||
} else {
|
||||
this._firstpin = null;
|
||||
this.askForPin = 1;
|
||||
$timeout(function() {
|
||||
self.clear();
|
||||
self.error = 'Entered PINs were not equal. Try again';
|
||||
var _self = self;
|
||||
$timeout(function() {
|
||||
_self.error = null;
|
||||
}, 2000);
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$scope.$emit('pin', pin);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.skip = function() {
|
||||
$scope.$emit('pin', null);
|
||||
};
|
||||
});
|
||||
54
src/js/controllers/preferences.js
Normal file
54
src/js/controllers/preferences.js
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesController',
|
||||
function($scope, $rootScope, $filter, $timeout, $modal, $log, configService, profileService) {
|
||||
this.error = null;
|
||||
this.success = null;
|
||||
|
||||
var config = configService.getSync();
|
||||
|
||||
this.unitName = config.wallet.settings.unitName;
|
||||
this.bwsurl = config.bws.url;
|
||||
this.selectedAlternative = {
|
||||
name: config.wallet.settings.alternativeName,
|
||||
isoCode: config.wallet.settings.alternativeIsoCode
|
||||
};
|
||||
var fc = profileService.focusedClient;
|
||||
$scope.encrypt = fc.hasPrivKeyEncrypted();
|
||||
|
||||
var unwatch = $scope.$watch('encrypt', function(val) {
|
||||
var fc = profileService.focusedClient;
|
||||
if (val && !fc.hasPrivKeyEncrypted()) {
|
||||
$rootScope.$emit('Local/NeedsPassword', true, function(err, password) {
|
||||
if (err || !password) {
|
||||
$scope.encrypt = false;
|
||||
return;
|
||||
}
|
||||
profileService.setPrivateKeyEncryptionFC(password, function() {
|
||||
$scope.encrypt = true;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (!val && fc.hasPrivKeyEncrypted()) {
|
||||
profileService.unlockFC(function(err){
|
||||
if (err) {
|
||||
$scope.encrypt = true;
|
||||
return;
|
||||
}
|
||||
profileService.disablePrivateKeyEncryptionFC(function(err) {
|
||||
if (err) {
|
||||
$scope.encrypt = true;
|
||||
$log.error(err);
|
||||
return;
|
||||
}
|
||||
$scope.encrypt = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
unwatch();
|
||||
});
|
||||
});
|
||||
57
src/js/controllers/preferencesAltCurrency.js
Normal file
57
src/js/controllers/preferencesAltCurrency.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesAltCurrencyController',
|
||||
function($scope, $rootScope, configService, go, rateService, lodash) {
|
||||
this.hideAdv = true;
|
||||
this.hidePriv = true;
|
||||
this.hideSecret = true;
|
||||
this.error = null;
|
||||
this.success = null;
|
||||
|
||||
var config = configService.getSync();
|
||||
|
||||
this.selectedAlternative = {
|
||||
name: config.wallet.settings.alternativeName,
|
||||
isoCode: config.wallet.settings.alternativeIsoCode
|
||||
};
|
||||
|
||||
this.alternativeOpts = [this.selectedAlternative]; //default value
|
||||
|
||||
var self = this;
|
||||
rateService.whenAvailable(function() {
|
||||
self.alternativeOpts = rateService.listAlternatives();
|
||||
lodash.remove(self.alternativeOpts, function(n) {
|
||||
return n.isoCode == 'BTC';
|
||||
});
|
||||
|
||||
for (var ii in self.alternativeOpts) {
|
||||
if (config.wallet.settings.alternativeIsoCode === self.alternativeOpts[ii].isoCode) {
|
||||
self.selectedAlternative = self.alternativeOpts[ii];
|
||||
}
|
||||
}
|
||||
$scope.$digest();
|
||||
});
|
||||
|
||||
|
||||
this.save = function(newAltCurrency) {
|
||||
var opts = {
|
||||
wallet: {
|
||||
settings: {
|
||||
alternativeName: newAltCurrency.name,
|
||||
alternativeIsoCode: newAltCurrency.isoCode,
|
||||
}
|
||||
}
|
||||
};
|
||||
this.selectedAlternative = {
|
||||
name: newAltCurrency.name,
|
||||
isoCode: newAltCurrency.isoCode,
|
||||
};
|
||||
|
||||
configService.set(opts, function(err) {
|
||||
if (err) console.log(err);
|
||||
$scope.$emit('Local/ConfigurationUpdated');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
33
src/js/controllers/preferencesBwsUrl.js
Normal file
33
src/js/controllers/preferencesBwsUrl.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesBwsUrlController',
|
||||
function($scope, $rootScope, $filter, $timeout, $modal, balanceService, notification, backupService, profileService, configService, isMobile, isCordova, go, rateService, applicationService, bwcService) {
|
||||
this.isSafari = isMobile.Safari();
|
||||
this.isCordova = isCordova;
|
||||
this.hideAdv = true;
|
||||
this.hidePriv = true;
|
||||
this.hideSecret = true;
|
||||
this.error = null;
|
||||
this.success = null;
|
||||
|
||||
var config = configService.getSync();
|
||||
|
||||
this.bwsurl = config.bws.url;
|
||||
|
||||
this.save = function() {
|
||||
var opts = {
|
||||
bws: {
|
||||
url: this.bwsurl,
|
||||
}
|
||||
};
|
||||
|
||||
configService.set(opts, function(err) {
|
||||
if (err) console.log(err);
|
||||
applicationService.restart(true);
|
||||
go.walletHome();
|
||||
$scope.$emit('Local/ConfigurationUpdated');
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
36
src/js/controllers/preferencesColor.js
Normal file
36
src/js/controllers/preferencesColor.js
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesColorController',
|
||||
function($scope, configService, profileService, go) {
|
||||
var config = configService.getSync();
|
||||
this.colorOpts = [
|
||||
'#1ABC9C',
|
||||
'#4A90E2',
|
||||
'#F39C12',
|
||||
'#FF3366',
|
||||
'#9B59B6',
|
||||
'#213140',
|
||||
];
|
||||
|
||||
var fc = profileService.focusedClient;
|
||||
var walletId = fc.credentials.walletId;
|
||||
|
||||
var config = configService.getSync();
|
||||
config.colorFor = config.colorFor || {};
|
||||
this.color = config.colorFor[walletId] || '#1ABC9C';
|
||||
|
||||
this.save = function(color) {
|
||||
var self = this;
|
||||
var opts = {
|
||||
colorFor: {}
|
||||
};
|
||||
opts.colorFor[walletId] = color;
|
||||
|
||||
configService.set(opts, function(err) {
|
||||
if (err) console.log(err);
|
||||
self.color = color;
|
||||
$scope.$emit('Local/ColorUpdated');
|
||||
});
|
||||
|
||||
};
|
||||
});
|
||||
72
src/js/controllers/preferencesDelete.js
Normal file
72
src/js/controllers/preferencesDelete.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesDeleteWalletController',
|
||||
function($scope, $rootScope, $filter, $timeout, $modal, notification, profileService, isCordova, go) {
|
||||
this.isCordova = isCordova;
|
||||
this.error = null;
|
||||
|
||||
var _modalDeleteWallet = function() {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.title = 'Are you sure you want to delete this wallet?';
|
||||
$scope.loading = false;
|
||||
|
||||
$scope.ok = function() {
|
||||
$scope.loading = true;
|
||||
$modalInstance.close('ok');
|
||||
|
||||
};
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/confirmation.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl
|
||||
});
|
||||
modalInstance.result.then(function(ok) {
|
||||
if (ok) {
|
||||
_deleteWallet();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var _deleteWallet = function() {
|
||||
$timeout(function() {
|
||||
var fc = profileService.focusedClient;
|
||||
var walletName = fc.credentials.walletName;
|
||||
|
||||
profileService.deleteWalletFC({}, function(err) {
|
||||
if (err) {
|
||||
this.error = err.message || err;
|
||||
console.log(err);
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
});
|
||||
} else {
|
||||
go.walletHome();
|
||||
$timeout(function() {
|
||||
notification.success('Success', 'The wallet "' + walletName + '" was deleted');
|
||||
});
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.deleteWallet = function() {
|
||||
if (isCordova) {
|
||||
navigator.notification.confirm(
|
||||
'Are you sure you want to delete this wallet?',
|
||||
function(buttonIndex) {
|
||||
if (buttonIndex == 2) {
|
||||
_deleteWallet();
|
||||
}
|
||||
},
|
||||
'Confirm', ['Cancel', 'OK']
|
||||
);
|
||||
} else {
|
||||
_modalDeleteWallet();
|
||||
}
|
||||
};
|
||||
});
|
||||
59
src/js/controllers/preferencesUnit.js
Normal file
59
src/js/controllers/preferencesUnit.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('preferencesUnitController',
|
||||
function($rootScope, $scope, configService, go) {
|
||||
var config = configService.getSync();
|
||||
this.unitName = config.wallet.settings.unitName;
|
||||
this.unitOpts = [
|
||||
// TODO : add Satoshis to bitcore-wallet-client formatAmount()
|
||||
// {
|
||||
// name: 'Satoshis (100,000,000 satoshis = 1BTC)',
|
||||
// shortName: 'SAT',
|
||||
// value: 1,
|
||||
// decimals: 0,
|
||||
// code: 'sat',
|
||||
// },
|
||||
{
|
||||
name: 'bits (1,000,000 bits = 1BTC)',
|
||||
shortName: 'bits',
|
||||
value: 100,
|
||||
decimals: 2,
|
||||
code: 'bit',
|
||||
}
|
||||
// TODO : add mBTC to bitcore-wallet-client formatAmount()
|
||||
// ,{
|
||||
// name: 'mBTC (1,000 mBTC = 1BTC)',
|
||||
// shortName: 'mBTC',
|
||||
// value: 100000,
|
||||
// decimals: 5,
|
||||
// code: 'mbtc',
|
||||
// }
|
||||
, {
|
||||
name: 'BTC',
|
||||
shortName: 'BTC',
|
||||
value: 100000000,
|
||||
decimals: 8,
|
||||
code: 'btc',
|
||||
}
|
||||
];
|
||||
|
||||
this.save = function(newUnit) {
|
||||
var opts = {
|
||||
wallet: {
|
||||
settings: {
|
||||
unitName: newUnit.shortName,
|
||||
unitToSatoshi: newUnit.value,
|
||||
unitDecimals: newUnit.decimals,
|
||||
unitCode: newUnit.code,
|
||||
}
|
||||
}
|
||||
};
|
||||
this.unitName = newUnit.shortName;
|
||||
|
||||
configService.set(opts, function(err) {
|
||||
if (err) console.log(err);
|
||||
$scope.$emit('Local/ConfigurationUpdated');
|
||||
});
|
||||
|
||||
};
|
||||
});
|
||||
68
src/js/controllers/profile.js
Normal file
68
src/js/controllers/profile.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('ProfileController', function($scope, $rootScope, $location, $modal, $filter, $timeout, backupService, identityService, isMobile, isCordova, notification) {
|
||||
$scope.username = $rootScope.iden.getName();
|
||||
$scope.isSafari = isMobile.Safari();
|
||||
$scope.isCordova = isCordova;
|
||||
|
||||
$rootScope.title = 'Profile';
|
||||
$scope.hideAdv = true;
|
||||
|
||||
$scope.downloadProfileBackup = function() {
|
||||
backupService.profileDownload($rootScope.iden);
|
||||
};
|
||||
|
||||
$scope.viewProfileBackup = function() {
|
||||
$scope.loading = true;
|
||||
$timeout(function() {
|
||||
$scope.backupProfilePlainText = backupService.profileEncrypted($rootScope.iden);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.copyProfileBackup = function() {
|
||||
var ep = backupService.profileEncrypted($rootScope.iden);
|
||||
window.cordova.plugins.clipboard.copy(ep);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
};
|
||||
|
||||
$scope.sendProfileBackup = function() {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.toast.showShortCenter('Preparing backup...');
|
||||
var name = $rootScope.iden.fullName;
|
||||
var ep = backupService.profileEncrypted($rootScope.iden);
|
||||
var properties = {
|
||||
subject: 'Copay Profile Backup: ' + name,
|
||||
body: 'Here is the encrypted backup of the profile ' + name + ': \n\n' + ep + '\n\n To import this backup, copy all text between {...}, including the symbols {}',
|
||||
isHtml: false
|
||||
};
|
||||
window.plugin.email.open(properties);
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
if ($rootScope.quotaPerItem) {
|
||||
$scope.perItem = $filter('noFractionNumber')($rootScope.quotaPerItem / 1000, 1);
|
||||
$scope.nrWallets = parseInt($rootScope.quotaItems) - 1;
|
||||
}
|
||||
// no need to add event handlers here. Wallet deletion is handle by callback.
|
||||
};
|
||||
|
||||
$scope.deleteProfile = function() {
|
||||
$scope.loading = true;
|
||||
identityService.deleteProfile(function(err, res) {
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
log.warn(err);
|
||||
notification.error('Error', 'Could not delete profile');
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
});
|
||||
} else {
|
||||
$location.path('/');
|
||||
$timeout(function() {
|
||||
notification.success('Success', 'Profile successfully deleted');
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
87
src/js/controllers/receive.js
Normal file
87
src/js/controllers/receive.js
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('receiveController',
|
||||
function($rootScope, $scope, $timeout, $modal, $log, isCordova, isMobile, profileService, storageService) {
|
||||
var self = this;
|
||||
var fc = profileService.focusedClient;
|
||||
|
||||
|
||||
this.isCordova = isCordova;
|
||||
self.addresses = [];
|
||||
|
||||
var newAddrListener = $rootScope.$on('Local/NeedNewAddress', function() {
|
||||
self.getAddress();
|
||||
});
|
||||
$scope.$on('$destroy', newAddrListener);
|
||||
|
||||
this.newAddress = function() {
|
||||
self.generatingAddress = true;
|
||||
fc.createAddress(function(err, addr) {
|
||||
if (err) {
|
||||
$log.debug('Creating address ERROR:', err);
|
||||
$scope.$emit('Local/ClientError', err);
|
||||
} else {
|
||||
self.addr = addr.address;
|
||||
storageService.storeLastAddress(fc.credentials.walletId, addr.address, function() {});
|
||||
}
|
||||
self.generatingAddress = false;
|
||||
$scope.$digest();
|
||||
});
|
||||
};
|
||||
|
||||
this.getAddress = function() {
|
||||
$timeout(function() {
|
||||
storageService.getLastAddress(fc.credentials.walletId, function(err, addr) {
|
||||
if (addr) {
|
||||
self.addr = addr;
|
||||
} else {
|
||||
self.newAddress();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.copyAddress = function(addr) {
|
||||
if (isCordova) {
|
||||
window.cordova.plugins.clipboard.copy('bitcoin:' + addr);
|
||||
window.plugins.toast.showShortCenter('Copied to clipboard');
|
||||
}
|
||||
};
|
||||
|
||||
this.shareAddress = function(addr) {
|
||||
if (isCordova) {
|
||||
if (isMobile.Android() || isMobile.Windows()) {
|
||||
window.ignoreMobilePause = true;
|
||||
}
|
||||
window.plugins.socialsharing.share('bitcoin:' + addr, null, null, null);
|
||||
}
|
||||
};
|
||||
|
||||
this.openAddressModal = function(address) {
|
||||
var self = this;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance, address) {
|
||||
$scope.address = address;
|
||||
$scope.isCordova = self.isCordova;
|
||||
$scope.copyAddress = function(addr) {
|
||||
self.copyAddress(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/qr-address.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
resolve: {
|
||||
address: function() {
|
||||
return address;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
499
src/js/controllers/send.js
Normal file
499
src/js/controllers/send.js
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('sendController',
|
||||
function($rootScope, $scope, $window, $timeout, $modal, $filter, $log, notification, isMobile, txStatus, isCordova, bitcore, profileService, configService, rateService) {
|
||||
var fc = profileService.focusedClient;
|
||||
var self = this;
|
||||
|
||||
this.init = function() {
|
||||
this.isMobile = isMobile.any();
|
||||
this.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
$rootScope.wpInputFocused = false;
|
||||
|
||||
$rootScope.title = fc.credentials.m > 1 ? 'Send Proposal' : 'Send';
|
||||
this.blockUx = false;
|
||||
this.error = this.success = null;
|
||||
|
||||
this.isRateAvailable = false;
|
||||
this.showScanner = false;
|
||||
this.isMobile = isMobile.any();
|
||||
|
||||
var paymentUri = $rootScope.$on('paymentUri', function(event, uri) {
|
||||
$timeout(function() {
|
||||
self.setForm(uri);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
var config = configService.getSync().wallet.settings;
|
||||
this.alternativeName = config.alternativeName;
|
||||
this.alternativeAmount = 0;
|
||||
this.alternativeIsoCode = config.alternativeIsoCode;
|
||||
this.unitToSatoshi = config.unitToSatoshi;
|
||||
this.unitDecimals = config.unitDecimals;
|
||||
this.unitName = config.unitName;
|
||||
|
||||
|
||||
rateService.whenAvailable(function() {
|
||||
self.isRateAvailable = true;
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
|
||||
var openScannerCordova = $rootScope.$on('dataScanned', function(event, data) {
|
||||
self.setForm(data);
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
openScannerCordova();
|
||||
paymentUri();
|
||||
});
|
||||
|
||||
this.setInputs();
|
||||
};
|
||||
|
||||
this.formFocus = function(what) {
|
||||
if (!this.isWindowsPhoneApp) return
|
||||
|
||||
if (!what) {
|
||||
$rootScope.wpInputFocused = false;
|
||||
this.hideAddress = false;
|
||||
this.hideAmount = false;
|
||||
|
||||
} else {
|
||||
$rootScope.wpInputFocused = true;
|
||||
if (what == 'amount') {
|
||||
this.hideAddress = true;
|
||||
} else if (what == 'msg') {
|
||||
this.hideAddress = true;
|
||||
this.hideAmount = true;
|
||||
}
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
this.setInputs = function() {
|
||||
var unitToSat = this.unitToSatoshi;
|
||||
var satToUnit = 1 / unitToSat;
|
||||
/**
|
||||
* 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 $scope.__alternative;
|
||||
},
|
||||
set: function(newValue) {
|
||||
$scope.__alternative = newValue;
|
||||
if (typeof(newValue) === 'number' && self.isRateAvailable) {
|
||||
$scope._amount = parseFloat((rateService.fromFiat(newValue, self.alternativeIsoCode) * satToUnit).toFixed(self.unitDecimals), 10);
|
||||
}
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
Object.defineProperty($scope,
|
||||
"_amount", {
|
||||
get: function() {
|
||||
return $scope.__amount;
|
||||
},
|
||||
set: function(newValue) {
|
||||
$scope.__amount = newValue;
|
||||
if (typeof(newValue) === 'number' && self.isRateAvailable) {
|
||||
$scope.__alternative = parseFloat((rateService.toFiat(newValue * self.unitToSatoshi, self.alternativeIsoCode)).toFixed(2), 10);
|
||||
} else {
|
||||
$scope.__alternative = 0;
|
||||
}
|
||||
self.alternativeAmount = $scope.__alternative;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
Object.defineProperty($scope,
|
||||
"_address", {
|
||||
get: function() {
|
||||
return $scope.__address;
|
||||
},
|
||||
set: function(newValue) {
|
||||
$scope.__address = self.onAddressChange(newValue);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
};
|
||||
|
||||
this.setError = function(err) {
|
||||
$log.warn(err);
|
||||
var errMessage = 'The transaction' + (fc.credentials.m > 1 ? ' proposal' : '') +
|
||||
|
||||
' could not be created: ' + (err.message ? err.message : err);
|
||||
|
||||
this.error = errMessage;
|
||||
|
||||
$timeout(function() {
|
||||
$scope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
|
||||
this.setOngoingProcess = function(name) {
|
||||
var self = this;
|
||||
$timeout(function() {
|
||||
self.onGoingProcess = name;
|
||||
$rootScope.$apply();
|
||||
})
|
||||
};
|
||||
|
||||
this.submitForm = function(form) {
|
||||
var unitToSat = this.unitToSatoshi;
|
||||
|
||||
if (form.$invalid) {
|
||||
this.error = 'Unable to send transaction proposal';
|
||||
return;
|
||||
}
|
||||
|
||||
if (fc.isPrivKeyEncrypted()) {
|
||||
profileService.unlockFC(function(err) {
|
||||
if (err) return self.setError(err);
|
||||
return self.submitForm(form);
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
self.blockUx = true;
|
||||
self.setOngoingProcess('Sending');
|
||||
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Creating transaction...', true);
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
var comment = form.comment.$modelValue;
|
||||
var paypro = self._paypro;
|
||||
var address, amount;
|
||||
|
||||
address = form.address.$modelValue;
|
||||
amount = parseInt((form.amount.$modelValue * unitToSat).toFixed(0));
|
||||
|
||||
fc.sendTxProposal({
|
||||
toAddress: address,
|
||||
amount: amount,
|
||||
message: comment,
|
||||
payProUrl: paypro ? paypro.url : null,
|
||||
}, function(err, txp) {
|
||||
self.setOngoingProcess();
|
||||
if (err) {
|
||||
profileService.lockFC();
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
self.blockUx = false;
|
||||
return self.setError(err);
|
||||
}
|
||||
|
||||
self.signAndBroadcast(txp, function(err) {
|
||||
self.setOngoingProcess();
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
self.blockUx = false;
|
||||
if (err) {
|
||||
profileService.lockFC();
|
||||
return self.setError(err);
|
||||
}
|
||||
self.resetForm(form);
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
this.signAndBroadcast = function(txp, cb) {
|
||||
self.setOngoingProcess('Signing');
|
||||
fc.signTxProposal(txp, function(err, signedTx) {
|
||||
profileService.lockFC();
|
||||
self.setOngoingProcess();
|
||||
|
||||
if (err) return cb(err);
|
||||
|
||||
if (signedTx.status == 'accepted') {
|
||||
self.setOngoingProcess('Broadcasting');
|
||||
fc.broadcastTxProposal(signedTx, function(err, btx) {
|
||||
self.setOngoingProcess();
|
||||
if (err) {
|
||||
$scope.error = 'Transaction not broadcasted. Please try again.';
|
||||
$scope.$digest();
|
||||
} else {
|
||||
txStatus.notify(btx);
|
||||
$scope.$emit('Local/TxProposalAction');
|
||||
}
|
||||
return cb();
|
||||
});
|
||||
} else {
|
||||
txStatus.notify(signedTx);
|
||||
$scope.$emit('Local/TxProposalAction');
|
||||
return cb();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.setTopAmount = function() {
|
||||
throw new Error('todo: setTopAmount');
|
||||
var form = $scope.sendForm;
|
||||
if (form) {
|
||||
form.amount.$setViewValue(w.balanceInfo.topAmount);
|
||||
form.amount.$render();
|
||||
form.amount.$isValid = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.setForm = function(to, amount, comment) {
|
||||
var form = $scope.sendForm;
|
||||
if (to) {
|
||||
form.address.$setViewValue(to);
|
||||
form.address.$isValid = true;
|
||||
form.address.$render();
|
||||
this.lockAddress = true;
|
||||
}
|
||||
|
||||
if (amount) {
|
||||
form.amount.$setViewValue("" + amount);
|
||||
form.amount.$isValid = true;
|
||||
form.amount.$render();
|
||||
this.lockAmount = true;
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
form.comment.$setViewValue(comment);
|
||||
form.comment.$isValid = true;
|
||||
form.comment.$render();
|
||||
}
|
||||
};
|
||||
|
||||
this.resetForm = function(form) {
|
||||
this.error = this.success = null;
|
||||
this.fetchingURL = null;
|
||||
this._paypro = null;
|
||||
|
||||
this.lockAddress = false;
|
||||
this.lockAmount = false;
|
||||
|
||||
this._amount = this._address = null;
|
||||
|
||||
if (form && form.amount) {
|
||||
form.amount.$pristine = true;
|
||||
form.amount.$setViewValue('');
|
||||
form.amount.$render();
|
||||
|
||||
form.comment.$setViewValue('');
|
||||
form.comment.$render();
|
||||
form.$setPristine();
|
||||
|
||||
if (form.address) {
|
||||
form.address.$pristine = true;
|
||||
form.address.$setViewValue('');
|
||||
form.address.$render();
|
||||
}
|
||||
}
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
this.openPPModal = function(paypro) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
var satToUnit = 1 / self.unitToSatoshi;
|
||||
$scope.paypro = paypro;
|
||||
$scope.alternative = self.alternativeAmount;
|
||||
$scope.alternativeIsoCode = self.alternativeIsoCode;
|
||||
$scope.isRateAvailable = self.isRateAvailable;
|
||||
$scope.unitTotal = (paypro.amount * satToUnit).toFixed(self.unitDecimals);
|
||||
$scope.unitName = self.unitName;
|
||||
$scope.color = fc.backgroundColor;
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/paypro.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
this.setFromPayPro = function(uri, form) {
|
||||
var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id;
|
||||
if (isChromeApp) {
|
||||
this.error = 'Payment Protocol not yet supported on ChromeApp';
|
||||
return;
|
||||
}
|
||||
|
||||
var satToUnit = 1 / this.unitToSatoshi;
|
||||
this.fetchingURL = uri;
|
||||
this.blockUx = true;
|
||||
var self = this;
|
||||
|
||||
$log.debug('Fetch PayPro Request...', uri);
|
||||
fc.fetchPayPro({
|
||||
payProUrl: uri,
|
||||
}, function(err, paypro) {
|
||||
$log.debug(paypro);
|
||||
self.blockUx = false;
|
||||
self.fetchingURL = null;
|
||||
|
||||
if (err) {
|
||||
$log.warn(err);
|
||||
self.resetForm(form);
|
||||
var msg = err.toString();
|
||||
if (msg.match('HTTP')) {
|
||||
msg = 'Could not fetch payment information';
|
||||
}
|
||||
self.error = msg;
|
||||
} else {
|
||||
self._paypro = paypro;
|
||||
self.setForm(paypro.toAddress, (paypro.amount * satToUnit).toFixed(self.unitDecimals),
|
||||
paypro.memo);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.setFromUri = function(uri) {
|
||||
function sanitizeUri(uri) {
|
||||
// Fixes when a region uses comma to separate decimals
|
||||
var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
|
||||
var match = regex.exec(uri);
|
||||
if (!match || match.length === 0) {
|
||||
return uri;
|
||||
}
|
||||
var value = match[0].replace(',', '.');
|
||||
var newUri = uri.replace(regex, value);
|
||||
return newUri;
|
||||
};
|
||||
|
||||
var satToUnit = 1 / this.unitToSatoshi;
|
||||
|
||||
uri = sanitizeUri(uri);
|
||||
|
||||
if (!bitcore.URI.isValid(uri)) {
|
||||
return uri;
|
||||
}
|
||||
var parsed = new bitcore.URI(uri);
|
||||
var addr = parsed.address.toString();
|
||||
var message = parsed.message;
|
||||
if (parsed.r)
|
||||
return this.setFromPayPro(parsed.r);
|
||||
|
||||
var amount = parsed.amount ?
|
||||
(parsed.amount.toFixed(0) * satToUnit).toFixed(this.unitDecimals) : 0;
|
||||
|
||||
this.setForm(addr, amount, message);
|
||||
return addr;
|
||||
};
|
||||
|
||||
this.onAddressChange = function(value) {
|
||||
this.error = this.success = null;
|
||||
if (!value) return '';
|
||||
|
||||
if (this._paypro)
|
||||
return value;
|
||||
|
||||
if (value.indexOf('bitcoin:') === 0) {
|
||||
return this.setFromUri(value);
|
||||
} else if (/^https?:\/\//.test(value)) {
|
||||
return this.setFromPayPro(value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
this.openAddressBook = function() {
|
||||
var w = $rootScope.wallet;
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/address-book.html',
|
||||
windowClass: 'full',
|
||||
controller: function($scope, $modalInstance) {
|
||||
|
||||
$scope.showForm = null;
|
||||
$scope.addressBook = w.addressBook;
|
||||
|
||||
$scope.hasEntry = function() {
|
||||
return _.keys($scope.addressBook).length > 0 ? true : false;
|
||||
};
|
||||
|
||||
$scope.toggleAddressBookEntry = function(key) {
|
||||
w.toggleAddressBookEntry(key);
|
||||
};
|
||||
|
||||
$scope.copyToSend = function(addr) {
|
||||
$modalInstance.close(addr);
|
||||
};
|
||||
|
||||
$scope.cancel = function(form) {
|
||||
$scope.error = $scope.success = $scope.newaddress = $scope.newlabel = null;
|
||||
clearForm(form);
|
||||
$scope.toggleForm();
|
||||
};
|
||||
|
||||
$scope.toggleForm = function() {
|
||||
$scope.showForm = !$scope.showForm;
|
||||
};
|
||||
|
||||
var clearForm = function(form) {
|
||||
form.newaddress.$pristine = true;
|
||||
form.newaddress.$setViewValue('');
|
||||
form.newaddress.$render();
|
||||
|
||||
form.newlabel.$pristine = true;
|
||||
form.newlabel.$setViewValue('');
|
||||
form.newlabel.$render();
|
||||
form.$setPristine();
|
||||
};
|
||||
|
||||
// TODO change to modal
|
||||
$scope.submitAddressBook = function(form) {
|
||||
if (form.$invalid) {
|
||||
return;
|
||||
}
|
||||
$scope.blockUx = true;
|
||||
$timeout(function() {
|
||||
var errorMsg;
|
||||
var entry = {
|
||||
"address": form.newaddress.$modelValue,
|
||||
"label": form.newlabel.$modelValue
|
||||
};
|
||||
try {
|
||||
w.setAddressBook(entry.address, entry.label);
|
||||
} catch (e) {
|
||||
$log.warn(e);
|
||||
errorMsg = e.message;
|
||||
}
|
||||
|
||||
if (errorMsg) {
|
||||
$scope.error = errorMsg;
|
||||
} else {
|
||||
clearForm(form);
|
||||
$scope.toggleForm();
|
||||
notification.success('Entry created', 'New addressbook entry created')
|
||||
}
|
||||
$scope.blockUx = false;
|
||||
$rootScope.$digest();
|
||||
}, 100);
|
||||
return;
|
||||
};
|
||||
|
||||
$scope.close = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(addr) {
|
||||
$scope.setForm(addr);
|
||||
});
|
||||
};
|
||||
});
|
||||
26
src/js/controllers/settings.js
Normal file
26
src/js/controllers/settings.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('settingsController', function(configService, applicationService) {
|
||||
var config;
|
||||
|
||||
this.init = function() {
|
||||
config = configService.get();
|
||||
this.bws = config.bws.url;
|
||||
};
|
||||
|
||||
this.save = function() {
|
||||
if (!this.bws) return;
|
||||
config.bws.url = this.bws;
|
||||
var res = configService.set(config);
|
||||
if (res) {
|
||||
applicationService.restart();
|
||||
}
|
||||
};
|
||||
|
||||
this.reset = function() {
|
||||
if (configService.reset()) {
|
||||
applicationService.restart();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
52
src/js/controllers/sidebar.js
Normal file
52
src/js/controllers/sidebar.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('sidebarController',
|
||||
function($rootScope, $timeout, lodash, profileService, configService, go) {
|
||||
var self = this;
|
||||
self.walletSelection = false;
|
||||
|
||||
// wallet list change
|
||||
$rootScope.$on('Local/WalletListUpdated', function(event) {
|
||||
self.walletSelection = false;
|
||||
self.setWallets();
|
||||
});
|
||||
|
||||
$rootScope.$on('Local/ColorUpdated', function(event) {
|
||||
self.setWallets();
|
||||
});
|
||||
|
||||
self.signout = function() {
|
||||
profileService.signout();
|
||||
};
|
||||
|
||||
self.switchWallet = function(wid) {
|
||||
self.walletSelection = false;
|
||||
profileService.setAndStoreFocus(wid, function() {});
|
||||
go.walletHome();
|
||||
};
|
||||
|
||||
self.toggleWalletSelection = function() {
|
||||
self.walletSelection = !self.walletSelection;
|
||||
if (!self.walletSelection) return;
|
||||
self.setWallets();
|
||||
};
|
||||
|
||||
self.setWallets = function() {
|
||||
if (!profileService.profile) return;
|
||||
var config = configService.getSync();
|
||||
config.colorFor = config.colorFor || {};
|
||||
var ret = lodash.map(profileService.profile.credentials, function(c) {
|
||||
return {
|
||||
m: c.m,
|
||||
n: c.n,
|
||||
name: c.walletName,
|
||||
id: c.walletId,
|
||||
color: config.colorFor[c.walletId] || '#1ABC9C',
|
||||
};
|
||||
});
|
||||
self.wallets = lodash.sortBy(ret, 'walletName');
|
||||
};
|
||||
|
||||
self.setWallets();
|
||||
|
||||
});
|
||||
5
src/js/controllers/signOut.js
Normal file
5
src/js/controllers/signOut.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
angular.module('copayApp.controllers').controller('signOutController', function(identityService) {
|
||||
|
||||
identityService.signout();
|
||||
|
||||
});
|
||||
174
src/js/controllers/signin.js
Normal file
174
src/js/controllers/signin.js
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('signinController',
|
||||
function($rootScope, $timeout, $window, go, notification, profileService, pinService, applicationService, isMobile, isCordova, localStorageService) {
|
||||
|
||||
var KEY = 'CopayDisclaimer';
|
||||
var _credentials;
|
||||
|
||||
this.init = function() {
|
||||
this.isMobile = isMobile.any();
|
||||
this.isWindowsPhoneApp = isMobile.Windows() && isCordova;
|
||||
this.hideForWP = 0;
|
||||
this.attempt = 0;
|
||||
this.digits = [];
|
||||
this.defined = [];
|
||||
this.askForPin = 0;
|
||||
|
||||
// This is only for backwards compat, insight api should link to #!/confirmed directly
|
||||
if (getParam('confirmed')) {
|
||||
var hashIndex = window.location.href.indexOf('/?');
|
||||
window.location = window.location.href.substr(0, hashIndex) + '#!/confirmed';
|
||||
return;
|
||||
}
|
||||
|
||||
if ($rootScope.fromEmailConfirmation) {
|
||||
this.confirmedEmail = true;
|
||||
$rootScope.fromEmailConfirmation = false;
|
||||
}
|
||||
|
||||
if ($rootScope.wallet) {
|
||||
go.walletHome();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.clear = function() {
|
||||
pinService.clearPin(this);
|
||||
};
|
||||
|
||||
this.press = function(digit) {
|
||||
pinService.pressPin(this, digit);
|
||||
};
|
||||
|
||||
this.skip = function() {
|
||||
pinService.skipPin(this);
|
||||
};
|
||||
|
||||
this.agreeDisclaimer = function() {
|
||||
if (localStorageService.set(KEY, true)) {
|
||||
this.showDisclaimer = null;
|
||||
}
|
||||
};
|
||||
|
||||
this.formFocus = function() {
|
||||
if (this.isWindowsPhoneApp) {
|
||||
this.hideForWP = true;
|
||||
$timeout(function() {
|
||||
this.$digest();
|
||||
}, 1);
|
||||
}
|
||||
};
|
||||
|
||||
this.openWithPin = function(pin) {
|
||||
|
||||
if (!pin) {
|
||||
this.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
var credentials = pinService.get(pin, function(err, credentials) {
|
||||
if (err || !credentials) {
|
||||
$rootScope.starting = null;
|
||||
this.error = 'Wrong PIN';
|
||||
this.clear();
|
||||
$timeout(function() {
|
||||
this.error = null;
|
||||
}, 2000);
|
||||
return;
|
||||
}
|
||||
this.open(credentials.email, credentials.password);
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openWallets = function() {
|
||||
var iden = $rootScope.iden;
|
||||
$rootScope.hideNavigation = false;
|
||||
$rootScope.starting = true;
|
||||
iden.openWallets();
|
||||
};
|
||||
|
||||
this.createPin = function(pin) {
|
||||
preconditions.checkArgument(pin);
|
||||
preconditions.checkState($rootScope.iden);
|
||||
preconditions.checkState(_credentials && _credentials.email);
|
||||
$rootScope.starting = true;
|
||||
|
||||
$timeout(function() {
|
||||
pinService.save(pin, _credentials.email, _credentials.password, function(err) {
|
||||
_credentials.password = '';
|
||||
_credentials = null;
|
||||
this.askForPin = 0;
|
||||
$rootScope.hasPin = true;
|
||||
$rootScope.starting = null;
|
||||
this.openWallets();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
this.openWithCredentials = function(form) {
|
||||
if (form && form.$invalid) {
|
||||
this.error = 'Please enter the required fields';
|
||||
return;
|
||||
}
|
||||
|
||||
this.open(form.email.$modelValue, form.password.$modelValue);
|
||||
};
|
||||
|
||||
|
||||
this.pinLogout = function() {
|
||||
pinService.clear(function() {
|
||||
copay.logger.debug('PIN erased');
|
||||
delete $rootScope['hasPin'];
|
||||
applicationService.restart();
|
||||
});
|
||||
};
|
||||
|
||||
this.open = function(username, password) {
|
||||
$rootScope.starting = true;
|
||||
profileService.open(username, password, function(err) {
|
||||
if (err) {
|
||||
$rootScope.starting = false;
|
||||
$rootScope.hasPin = false;
|
||||
pinService.clear(function() {});
|
||||
this.error = 'Unknown error';
|
||||
return;
|
||||
}
|
||||
|
||||
// mobile
|
||||
if (this.isMobile && !$rootScope.hasPin) {
|
||||
_credentials = {
|
||||
email: email,
|
||||
password: password,
|
||||
};
|
||||
this.askForPin = 1;
|
||||
$rootScope.starting = false;
|
||||
$rootScope.hideNavigation = true;
|
||||
$timeout(function() {
|
||||
$rootScope.$digest();
|
||||
}, 1);
|
||||
}
|
||||
// no mobile
|
||||
else {
|
||||
// this.openWallets();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getParam(sname) {
|
||||
var params = location.search.substr(location.search.indexOf("?") + 1);
|
||||
var sval = "";
|
||||
params = params.split("&");
|
||||
// split param and value into individual pieces
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var temp = params[i].split("=");
|
||||
if ([temp[0]] == sname) {
|
||||
sval = temp[1];
|
||||
}
|
||||
}
|
||||
return sval;
|
||||
}
|
||||
});
|
||||
127
src/js/controllers/topbar.js
Normal file
127
src/js/controllers/topbar.js
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('topbarController', function($rootScope, $scope, $timeout, $modal, isCordova, isMobile, go) {
|
||||
var cordovaOpenScanner = function() {
|
||||
window.ignoreMobilePause = true;
|
||||
cordova.plugins.barcodeScanner.scan(
|
||||
function onSuccess(result) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
if (result.cancelled) return;
|
||||
|
||||
$timeout(function() {
|
||||
var data = result.text;
|
||||
$rootScope.$emit('dataScanned', data);
|
||||
}, 1000);
|
||||
},
|
||||
function onError(error) {
|
||||
$timeout(function() {
|
||||
window.ignoreMobilePause = false;
|
||||
}, 100);
|
||||
alert('Scanning error');
|
||||
});
|
||||
go.send();
|
||||
};
|
||||
|
||||
var modalOpenScanner = function() {
|
||||
var _scope = $scope;
|
||||
var ModalInstanceCtrl = function($scope, $rootScope, $modalInstance) {
|
||||
// QR code Scanner
|
||||
var video;
|
||||
var canvas;
|
||||
var $video;
|
||||
var context;
|
||||
var localMediaStream;
|
||||
|
||||
var _scan = function(evt) {
|
||||
if (localMediaStream) {
|
||||
context.drawImage(video, 0, 0, 300, 225);
|
||||
try {
|
||||
qrcode.decode();
|
||||
} catch (e) {
|
||||
//qrcodeError(e);
|
||||
}
|
||||
}
|
||||
$timeout(_scan, 500);
|
||||
};
|
||||
|
||||
var _scanStop = function() {
|
||||
if (localMediaStream && localMediaStream.stop) localMediaStream.stop();
|
||||
localMediaStream = null;
|
||||
video.src = '';
|
||||
};
|
||||
|
||||
qrcode.callback = function(data) {
|
||||
_scanStop();
|
||||
$modalInstance.close(data);
|
||||
};
|
||||
|
||||
var _successCallback = function(stream) {
|
||||
video.src = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
localMediaStream = stream;
|
||||
video.play();
|
||||
$timeout(_scan, 1000);
|
||||
};
|
||||
|
||||
var _videoError = function(err) {
|
||||
$scope.cancel();
|
||||
};
|
||||
|
||||
var setScanner = function() {
|
||||
navigator.getUserMedia = navigator.getUserMedia ||
|
||||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
|
||||
navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL ||
|
||||
window.mozURL || window.msURL;
|
||||
};
|
||||
|
||||
$scope.init = function() {
|
||||
setScanner();
|
||||
$timeout(function() {
|
||||
go.send();
|
||||
canvas = document.getElementById('qr-canvas');
|
||||
context = canvas.getContext('2d');
|
||||
|
||||
|
||||
video = document.getElementById('qrcode-scanner-video');
|
||||
$video = angular.element(video);
|
||||
canvas.width = 300;
|
||||
canvas.height = 225;
|
||||
context.clearRect(0, 0, 300, 225);
|
||||
|
||||
navigator.getUserMedia({
|
||||
video: true
|
||||
}, _successCallback, _videoError);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
_scanStop();
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/scanner.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
backdrop : 'static',
|
||||
keyboard: false
|
||||
});
|
||||
modalInstance.result.then(function(data) {
|
||||
$rootScope.$emit('dataScanned', data);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
this.openScanner = function() {
|
||||
if (isCordova) {
|
||||
cordovaOpenScanner();
|
||||
}
|
||||
else {
|
||||
modalOpenScanner();
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
7
src/js/controllers/unsupported.js
Normal file
7
src/js/controllers/unsupported.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('unsupportedController', function($state) {
|
||||
if (localStorage && localStorage.length > 0) {
|
||||
$state.go('signin');
|
||||
}
|
||||
});
|
||||
6
src/js/controllers/version.js
Normal file
6
src/js/controllers/version.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('versionController', function() {
|
||||
this.version = window.version;
|
||||
this.commitHash = window.commitHash;
|
||||
});
|
||||
47
src/js/controllers/walletForPayment.js
Normal file
47
src/js/controllers/walletForPayment.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('walletForPaymentController', function($rootScope, $scope, $modal, identityService, go) {
|
||||
|
||||
// INIT: (not it a function, since there is no associated html)
|
||||
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance, identityService) {
|
||||
$scope.loading = true;
|
||||
preconditions.checkState($rootScope.iden);
|
||||
|
||||
var iden = $rootScope.iden;
|
||||
iden.on('newWallet', function() {
|
||||
$scope.setWallets();
|
||||
});
|
||||
|
||||
$scope.setWallets = function() {
|
||||
$scope.wallets = $rootScope.iden.getWallets();
|
||||
};
|
||||
|
||||
$scope.ok = function(w) {
|
||||
$modalInstance.close(w);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$rootScope.pendingPayment = null;
|
||||
$modalInstance.close();
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/walletSelection.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(w) {
|
||||
if (w) {
|
||||
identityService.setFocusedWallet(w);
|
||||
$rootScope.walletForPaymentSet = true;
|
||||
} else {
|
||||
$rootScope.pendingPayment = null;
|
||||
}
|
||||
go.walletHome();
|
||||
}, function() {
|
||||
$rootScope.pendingPayment = null;
|
||||
go.walletHome();
|
||||
});
|
||||
});
|
||||
213
src/js/controllers/walletHome.js
Normal file
213
src/js/controllers/walletHome.js
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
'use strict';
|
||||
|
||||
|
||||
// TODO rateService
|
||||
angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $timeout, $filter, $modal, notification, txStatus, isCordova, profileService, lodash) {
|
||||
|
||||
|
||||
$scope.openCopayersModal = function(copayers, copayerId) {
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.copayers = copayers;
|
||||
$scope.copayerId = copayerId;
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
$modal.open({
|
||||
templateUrl: 'views/modals/copayers.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
$scope.openTxModal = function(tx, copayers) {
|
||||
var fc = profileService.focusedClient;
|
||||
var ModalInstanceCtrl = function($scope, $modalInstance) {
|
||||
$scope.error = null;
|
||||
$scope.tx = tx;
|
||||
$scope.copayers = copayers
|
||||
$scope.loading = null;
|
||||
$scope.color = fc.backgroundColor;
|
||||
|
||||
$scope.getShortNetworkName = function() {
|
||||
return fc.credentials.networkName.substring(0, 4);
|
||||
};
|
||||
|
||||
lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy', 'transactionProposalRemoved', 'TxProposalRemoved'], function(eventName) {
|
||||
$rootScope.$on(eventName, function() {
|
||||
fc.getTx($scope.tx.id, function(err, tx) {
|
||||
if (err) {
|
||||
|
||||
if (err.code && err.code == 'BADREQUEST' &&
|
||||
(eventName == 'transactionProposalRemoved' || eventName == 'TxProposalRemoved')) {
|
||||
$scope.tx.removed = true;
|
||||
$scope.tx.couldRemove = false;
|
||||
$scope.tx.pendingForUs = false;
|
||||
$scope.$apply();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var action = lodash.find(tx.actions, {
|
||||
copayerId: fc.credentials.copayerId
|
||||
});
|
||||
$scope.tx = tx;
|
||||
if (!action && tx.status == 'pending')
|
||||
$scope.tx.pendingForUs = true;
|
||||
$scope.updateCopayerList();
|
||||
$scope.$apply();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$scope.updateCopayerList = function() {
|
||||
lodash.map($scope.copayers, function(cp) {
|
||||
lodash.each($scope.tx.actions, function(ac) {
|
||||
if (cp.id == ac.copayerId) {
|
||||
cp.action = ac.type;
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.sign = function(txp) {
|
||||
var fc = profileService.focusedClient;
|
||||
if (fc.isPrivKeyEncrypted()) {
|
||||
profileService.unlockFC(function(err) {
|
||||
if (err) {
|
||||
$scope.error = err;
|
||||
return;
|
||||
}
|
||||
return $scope.sign(txp);
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Signing transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
fc.signTxProposal(txp, function(err, txpsi) {
|
||||
profileService.lockFC();
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = err.message || 'Transaction not signed. Please try again.';
|
||||
$scope.$digest();
|
||||
} else {
|
||||
//if txp has required signatures then broadcast it
|
||||
var txpHasRequiredSignatures = txpsi.status == 'accepted';
|
||||
if (txpHasRequiredSignatures) {
|
||||
fc.broadcastTxProposal(txpsi, function(err, txpsb) {
|
||||
if (err) {
|
||||
$scope.error = 'Transaction not broadcasted. Please try again.';
|
||||
$scope.$digest();
|
||||
} else {
|
||||
$modalInstance.close(txpsb);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$modalInstance.close(txpsi);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.reject = function(txp) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Rejecting transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
fc.rejectTxProposal(txp, null, function(err, txpr) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = err.message || 'Transaction not rejected. Please try again.';
|
||||
$scope.$digest();
|
||||
} else {
|
||||
$modalInstance.close(txpr);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
|
||||
$scope.remove = function(txp) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Deleting transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
fc.removeTxProposal(txp, function(err, txpb) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
|
||||
// Hacky: request tries to parse an empty response
|
||||
if (err && !(err.message && err.message.match(/Unexpected/)) ) {
|
||||
$scope.error = err.message || 'Transaction could not be deleted. Please try again.';
|
||||
$scope.$digest();
|
||||
return;
|
||||
}
|
||||
$modalInstance.close();
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.broadcast = function(txp) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.show(null, 'Sending transaction...', true);
|
||||
}
|
||||
$scope.loading = true;
|
||||
$scope.error = null;
|
||||
$timeout(function() {
|
||||
fc.broadcastTxProposal(txp, function(err, txpb) {
|
||||
if (isCordova) {
|
||||
window.plugins.spinnerDialog.hide();
|
||||
}
|
||||
$scope.loading = false;
|
||||
if (err) {
|
||||
$scope.error = err.message || 'Transaction not sent. Please try again.';
|
||||
$scope.$digest();
|
||||
} else {
|
||||
$modalInstance.close(txpb);
|
||||
}
|
||||
});
|
||||
}, 100);
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
};
|
||||
|
||||
var modalInstance = $modal.open({
|
||||
templateUrl: 'views/modals/txp-details.html',
|
||||
windowClass: 'full',
|
||||
controller: ModalInstanceCtrl,
|
||||
});
|
||||
|
||||
modalInstance.result.then(function(txp) {
|
||||
if (txp) {
|
||||
txStatus.notify(txp);
|
||||
}
|
||||
$scope.$emit('Local/TxProposalAction');
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
27
src/js/controllers/warning.js
Normal file
27
src/js/controllers/warning.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
angular.module('copayApp.controllers').controller('WarningController', function($scope, $rootScope, $location, identityService) {
|
||||
|
||||
$scope.checkLock = function() {
|
||||
if (!$rootScope.tmp || !$rootScope.tmp.getLock()) {
|
||||
console.log('[warning.js.7] TODO LOCK'); //TODO
|
||||
}
|
||||
};
|
||||
|
||||
$scope.signout = function() {
|
||||
identityService.signout();
|
||||
};
|
||||
|
||||
$scope.ignoreLock = function() {
|
||||
var w = $rootScope.tmp;
|
||||
delete $rootScope['tmp'];
|
||||
|
||||
if (!w) {
|
||||
$location.path('/');
|
||||
} else {
|
||||
w.ignoreLock = 1;
|
||||
$scope.loading = true;
|
||||
//controllerUtils.startNetwork(w, $scope);
|
||||
// TODO
|
||||
}
|
||||
};
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue