diff --git a/Gruntfile.js b/Gruntfile.js index 099cd23bd..540a676b0 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -43,6 +43,7 @@ module.exports = function(grunt) { scripts: { files: [ 'js/models/**/*.js', + 'js/models/*.js', 'plugins/*.js', 'copay.js', 'utils/*.js' diff --git a/config.js b/config.js index 10b5dafe7..94dc2a829 100644 --- a/config.js +++ b/config.js @@ -53,12 +53,13 @@ var defaultConfig = { verbose: 1, plugins: { - LocalStorage: true, - // GoogleDrive: true, +// LocalStorage: true, + GoogleDrive: true, }, GoogleDrive: { - clientId: '1', + clientId: '232630733383-29u1khqf5i8qubhf0homhpb2m14b5lja.apps.googleusercontent.com', + home: 'copay' }, }; if (typeof module !== 'undefined') diff --git a/js/app.js b/js/app.js index e1a3826ee..216553f20 100644 --- a/js/app.js +++ b/js/app.js @@ -37,11 +37,10 @@ var modules = [ 'copayApp.directives', ]; -if (config.plugins.length) +if (Object.keys(config.plugins).length) modules.push('angularLoad'); - var copayApp = window.copayApp = angular.module('copayApp', modules); copayApp.config(function($sceDelegateProvider) { diff --git a/js/controllers/home.js b/js/controllers/home.js index 2ac3e7381..c8a73d862 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,10 +1,13 @@ 'use strict'; -angular.module('copayApp.controllers').controller('HomeController', - function($scope, $rootScope, $location, walletFactory, notification, controllerUtils) { - - controllerUtils.redirIfLogged(); +angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, walletFactory, notification, controllerUtils) { + controllerUtils.redirIfLogged(); + + $scope.loading = true; + + walletFactory.getWallets(function(ret) { $scope.loading = false; - $scope.hasWallets = (walletFactory.getWallets() && walletFactory.getWallets().length > 0) ? true : false; + $scope.hasWallets = (ret && ret.length > 0) ? true : false; }); +}); diff --git a/js/controllers/open.js b/js/controllers/open.js index 019932988..005bba83d 100644 --- a/js/controllers/open.js +++ b/js/controllers/open.js @@ -14,12 +14,18 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc }; $rootScope.fromSetup = false; $scope.loading = false; - $scope.wallets = walletFactory.getWallets().sort(cmp); - $scope.selectedWalletId = walletFactory.storage.getLastOpened() || ($scope.wallets[0] && $scope.wallets[0].id); + walletFactory.getWallets(function(wallets) { + $scope.wallets = wallets.sort(cmp); + }); + + walletFactory.storage.getLastOpened(function(ret) { + $scope.selectedWalletId = ret || ($scope.wallets[0] && $scope.wallets[0].id); + }); + $scope.openPassword = ''; $scope.isMobile = !!window.cordova; - if (!$scope.wallets.length){ + if (!$scope.wallets.length) { $location.path('/'); } @@ -34,19 +40,15 @@ angular.module('copayApp.controllers').controller('OpenController', function($sc Passphrase.getBase64Async(password, function(passphrase) { var w, errMsg; - try { - w = walletFactory.open($scope.selectedWalletId, passphrase); - } catch (e) { - errMsg = e.message; - }; - if (!w) { - $scope.loading = false; - notification.error('Error', errMsg || 'Wrong password'); - $rootScope.$digest(); - return; - } - $rootScope.updatingBalance = true; - controllerUtils.startNetwork(w, $scope); + walletFactory.open($scope.selectedWalletId, passphrase, function(err, w) { + if (!w) { + $scope.loading = false; + notification.error('Error', err.errMsg || 'Wrong password'); + $rootScope.$digest(); + } + $rootScope.updatingBalance = true; + controllerUtils.startNetwork(w, $scope); + }); }); }; diff --git a/js/models/Storage.js b/js/models/Storage.js index 6a0c378e7..17757192f 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -1,5 +1,5 @@ 'use strict'; - +var preconditions = require('preconditions').singleton(); var CryptoJS = require('node-cryptojs-aes').CryptoJS; var bitcore = require('bitcore'); var preconditions = require('preconditions').instance(); @@ -55,38 +55,50 @@ Storage.prototype._decrypt = function(base64) { }; -Storage.prototype._read = function(k) { - var ret; - ret = this.storage.getItem(k); - if (!ret) return null; - ret = this._decrypt(ret); - if (!ret) return null; - ret = ret.toString(CryptoJS.enc.Utf8); - ret = JSON.parse(ret); - return ret; +Storage.prototype._read = function(k, cb) { + preconditions.checkArgument(cb); + + var self = this; + this.storage.getItem(k, function(ret) { + if (!ret) return cb(null); + var ret = self._decrypt(ret); + if (!ret) return cb(null); + + ret = ret.toString(CryptoJS.enc.Utf8); + ret = JSON.parse(ret); + return cb(ret); + }); }; -Storage.prototype._write = function(k, v) { +Storage.prototype._write = function(k, v, cb) { + preconditions.checkArgument(cb); + v = JSON.stringify(v); v = this._encrypt(v); - - this.storage.setItem(k, v); + this.storage.setItem(k, v, cb); }; // get value by key -Storage.prototype.getGlobal = function(k) { - var item = this.storage.getItem(k); - return item == 'undefined' ? undefined : item; +Storage.prototype.getGlobal = function(k, cb) { + preconditions.checkArgument(cb); + + this.storage.getItem(k, function(item) { + cb(item == 'undefined' ? undefined : item); + }); }; // set value for key -Storage.prototype.setGlobal = function(k, v) { - this.storage.setItem(k, typeof v === 'object' ? JSON.stringify(v) : v); +Storage.prototype.setGlobal = function(k, v, cb) { + preconditions.checkArgument(cb); + + this.storage.setItem(k, typeof v === 'object' ? JSON.stringify(v) : v, cb); }; // remove value for key -Storage.prototype.removeGlobal = function(k) { - this.storage.removeItem(k); +Storage.prototype.removeGlobal = function(k, cb) { + preconditions.checkArgument(cb); + + this.storage.removeItem(k, cb); }; Storage.prototype.getSessionId = function() { @@ -102,23 +114,38 @@ Storage.prototype._key = function(walletId, k) { return walletId + '::' + k; }; // get value by key -Storage.prototype.get = function(walletId, k) { - var ret = this._read(this._key(walletId, k)); - return ret; +Storage.prototype.get = function(walletId, k, cb) { + this._read(this._key(walletId, k), cb); +}; + +Storage.prototype.getMany = function(walletId, keys, cb) { + preconditions.checkArgument(cb); + + var self = this; + var ret = {}; + + var l = keys.length - 1; + for (var ii in keys) { + var k = keys[ii]; + this._read(this._key(walletId, k), function(v) { + ret[k] = v; + if (ii == l) return cb(ret); + }); + } }; // set value for key -Storage.prototype.set = function(walletId, k, v) { - this._write(this._key(walletId, k), v); +Storage.prototype.set = function(walletId, k, v, cb) { + this._write(this._key(walletId, k), v, cb); }; // remove value for key -Storage.prototype.remove = function(walletId, k) { - this.removeGlobal(this._key(walletId, k)); +Storage.prototype.remove = function(walletId, k, cb) { + this.removeGlobal(this._key(walletId, k), cb); }; -Storage.prototype.setName = function(walletId, name) { - this.setGlobal('nameFor::' + walletId, name); +Storage.prototype.setName = function(walletId, name, cb) { + this.setGlobal('nameFor::' + walletId, name, cb); }; Storage.prototype.getName = function(walletId) { @@ -126,41 +153,54 @@ Storage.prototype.getName = function(walletId) { return ret; }; -Storage.prototype.getWalletIds = function() { +Storage.prototype.getWalletIds = function(cb) { var walletIds = []; var uniq = {}; -console.log('[Encrypted.js.144]', this.storage); //TODO -console.log('[Encrypted.js.144]', this.storage.length); //TODO - for (var i = 0; i < this.storage.length; i++) { - var key = this.storage.key(i); - var split = key.split('::'); - if (split.length == 2) { - var walletId = split[0]; + this.storage.allKeys(function(keys) { +console.log('[Storage.js.170:keys:]',keys); //TODO - if (!walletId || walletId === 'nameFor' || walletId === 'lock') - continue; + for (var ii in keys) { + var key = keys[ii]; +console.log('[Storage.js.174:key:]',key); //TODO + var split = key.split('::'); + if (split.length == 2) { + var walletId = split[0]; - if (typeof uniq[walletId] === 'undefined') { - walletIds.push(walletId); - uniq[walletId] = 1; + if (!walletId || walletId === 'nameFor' || walletId === 'lock') + continue; + + if (typeof uniq[walletId] === 'undefined') { + walletIds.push(walletId); + uniq[walletId] = 1; + } } } - } - return walletIds; + return cb(walletIds); + }); }; -Storage.prototype.getWallets = function() { +Storage.prototype.getWallets = function(cb) { var wallets = []; - var ids = this.getWalletIds(); + var self = this; - for (var i in ids) { - wallets.push({ - id: ids[i], - name: this.getName(ids[i]), - }); - } - return wallets; + this.getWalletIds(function(ids) { + var l = ids.length - 1; + if (!l) + return cb([]); + + for (var i in ids) { + self.getName(ids[i], function(name) { + wallets.push({ + id: ids[i], + name: name, + }); + if (i == l) { + return cb(wallets); + } + }) + } + }); }; Storage.prototype.deleteWallet = function(walletId) { @@ -179,25 +219,35 @@ Storage.prototype.deleteWallet = function(walletId) { } }; -Storage.prototype.setLastOpened = function(walletId) { - this.setGlobal('lastOpened', walletId); +Storage.prototype.setLastOpened = function(walletId, cb) { + this.setGlobal('lastOpened', walletId, cb); } -Storage.prototype.getLastOpened = function() { - return this.getGlobal('lastOpened'); +Storage.prototype.getLastOpened = function(cb) { + this.getGlobal('lastOpened', cb); } //obj contains keys to be set -Storage.prototype.setFromObj = function(walletId, obj) { +Storage.prototype.setFromObj = function(walletId, obj, cb) { + preconditions.checkArgument(cb); + var self = this; + + console.log('[Storage.js.241]'); //TODO + var l = Object.keys(obj).length, + i = 0; for (var k in obj) { - this.set(walletId, k, obj[k]); + console.log('[Storage.js.247]', k, i, l); //TODO + self.set(walletId, k, obj[k], function() { + if (++i == l) { + self.setName(walletId, obj.opts.name, cb); + } + }); } - this.setName(walletId, obj.opts.name); }; // remove all values -Storage.prototype.clearAll = function() { - this.storage.clear(); +Storage.prototype.clearAll = function(cb) { + this.storage.clear(cb); }; Storage.prototype.import = function(base64) { diff --git a/js/models/core/PluginManager.js b/js/models/core/PluginManager.js index 23b3c2a77..bce94e464 100644 --- a/js/models/core/PluginManager.js +++ b/js/models/core/PluginManager.js @@ -3,8 +3,9 @@ var preconditions = require('preconditions').singleton(); function PluginManager(config) { this.registered = {}; + this.scripts = []; - for(var ii in config.plugins){ + for (var ii in config.plugins) { var pluginName = ii; if (!config.plugins[pluginName]) @@ -12,7 +13,7 @@ function PluginManager(config) { console.log('Loading plugin: ' + pluginName); var pluginClass = require('../plugins/' + pluginName); - var pluginObj = new pluginClass(); + var pluginObj = new pluginClass(config[pluginName]); pluginObj.init(); this._register(pluginObj, pluginName); } @@ -25,9 +26,10 @@ PluginManager.TYPE = {}; PluginManager.TYPE['STORAGE'] = KIND_UNIQUE; PluginManager.prototype._register = function(obj, name) { - preconditions.checkArgument(obj.type,'Plugin has not type:' + name); + preconditions.checkArgument(obj.type, 'Plugin has not type:' + name); var type = obj.type; var kind = PluginManager.TYPE[type]; + preconditions.checkArgument(kind, 'Plugin has unkown type' + name); preconditions.checkState(kind !== PluginManager.KIND_UNIQUE || !this.registered[type], 'Plugin kind already registered: ' + name); @@ -37,8 +39,11 @@ PluginManager.prototype._register = function(obj, name) { this.registered[type] = this.registered[type] || []; this.registered[type].push(obj); } + + this.scripts = this.scripts.concat(obj.scripts || []); }; + PluginManager.prototype.get = function(type) { return this.registered[type]; }; diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index 61e81b137..6ab010c5a 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -821,21 +821,28 @@ Wallet.prototype.getRegisteredPeerIds = function() { * @emits locked - in case the wallet is opened in another instance */ Wallet.prototype.keepAlive = function() { - try { - this.lock.keepAlive(); - } catch (e) { - log.debug(e); - this.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.'); - } + var self = this; + + this.lock.keepAlive(function(err) { + if (err) { + log.debug(err); + self.emit('locked', null, 'Wallet appears to be openned on other browser instance. Closing this one.'); + } + }); }; /** * @desc Store the wallet's state + * @param {function} callback (err) */ -Wallet.prototype.store = function() { +Wallet.prototype.store = function(cb) { + var self = this; this.keepAlive(); - this.storage.setFromObj(this.id, this.toObj()); - log.debug('Wallet stored'); + this.storage.setFromObj(this.id, this.toObj(), function(err) { + log.debug('Wallet stored'); + if (cb) + cb(err); + }); }; /** @@ -2339,11 +2346,14 @@ Wallet.prototype.indexDiscovery = function(start, change, copayerIndex, gap, cb) /** * @desc Closes the wallet and disconnects all services */ -Wallet.prototype.close = function() { +Wallet.prototype.close = function(cb) { + var self =this; log.debug('## CLOSING'); - this.lock.release(); - this.network.cleanUp(); - this.blockchain.destroy(); + this.lock.release(function() { + self.network.cleanUp(); + self.blockchain.destroy(); + if (cb) return cb(); + }); }; /** diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index 2efea81de..f164c533f 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -1,4 +1,5 @@ 'use strict'; +var preconditions = require('preconditions').singleton(); var TxProposals = require('./TxProposals'); var PublicKeyRing = require('./PublicKeyRing'); @@ -6,7 +7,7 @@ var PrivateKey = require('./PrivateKey'); var Wallet = require('./Wallet'); var _ = require('underscore'); var log = require('../../log'); -var PluginManager = require('./PluginManager'); +var PluginManager = require('./PluginManager'); var Async = module.exports.Async = require('../network/Async'); var Insight = module.exports.Insight = require('../blockchain/Insight'); var preconditions = require('preconditions').singleton(); @@ -33,16 +34,24 @@ var Storage = module.exports.Storage = require('../Storage'); * @param {string} version - the version of copay for which this wallet was generated (for example, 0.4.7) * @constructor */ -function WalletFactory(config, version) { + +function WalletFactory(config, version, pluginManager) { var self = this; config = config || {}; - this.pluginManager = new PluginManager(config); - - this.Storage = config.Storage || Storage; + this.Storage = config.Storage || Storage; this.Network = config.Network || Async; this.Blockchain = config.Blockchain || Insight; - this.storage = new this.Storage({storage: this.pluginManager.get('STORAGE')}); + + var storageOpts = {}; + + if (pluginManager) { + storageOpts = { + storage: pluginManager.get('STORAGE') + }; + } + + this.storage = new this.Storage(storageOpts); this.networks = { 'livenet': new this.Network(config.network.livenet), @@ -57,27 +66,6 @@ function WalletFactory(config, version) { this.version = version; }; -/** - * @desc - * Returns true if the storage instance can retrieve the following keys using a given walletId - *