This commit is contained in:
Matias Alejo Garcia 2015-03-06 12:00:10 -03:00
commit 320de62f13
348 changed files with 7745 additions and 30874 deletions

View 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);
};
});

View 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);
}
};
});

View 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;
});
});

View 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);
// });
});

View 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);
});

View file

@ -0,0 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('EmailConfirmationController', function($scope, $rootScope, $location) {
$rootScope.fromEmailConfirmation = true;
$location.path('/');
});

View 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');
};
});

View 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);
}
};
});

View 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...
});

View 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
View 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
View 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);
}
});

View 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);
};
});

View 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);
};
});

View 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);
};
});

View 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);
};
});

View 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();
});
});

View 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');
});
};
});

View 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');
});
};
});

View 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');
});
};
});

View 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();
}
};
});

View 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');
});
};
});

View 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');
});
}
});
};
});

View 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
View 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);
});
};
});

View 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();
}
};
});

View 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();
});

View file

@ -0,0 +1,5 @@
angular.module('copayApp.controllers').controller('signOutController', function(identityService) {
identityService.signout();
});

View 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;
}
});

View 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();
}
};
});

View file

@ -0,0 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('unsupportedController', function($state) {
if (localStorage && localStorage.length > 0) {
$state.go('signin');
}
});

View file

@ -0,0 +1,6 @@
'use strict';
angular.module('copayApp.controllers').controller('versionController', function() {
this.version = window.version;
this.commitHash = window.commitHash;
});

View 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();
});
});

View 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');
});
};
});

View 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
}
};
});