From 92f1bacf82516d51ed9dada27ad29cac9686b23c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 1 Oct 2014 08:35:17 -0300 Subject: [PATCH] login working on the UX --- js/controllers/home.js | 28 ++++++++++++++++++++++------ js/models/Identity.js | 40 +++++++++++++++++++++++++++------------- js/models/Profile.js | 28 +++++++++++++++------------- js/models/Storage.js | 2 +- js/models/Wallet.js | 17 ++++++++++++----- test/PayPro.js | 12 +++++++++++- test/Wallet.js | 21 +++++++++++---------- test/test.Identity.js | 36 +++++++++++++++++++----------------- test/test.Profile.js | 2 +- views/home.html | 11 ++++++----- 10 files changed, 125 insertions(+), 72 deletions(-) diff --git a/js/controllers/home.js b/js/controllers/home.js index 64580f85f..39c28adb1 100644 --- a/js/controllers/home.js +++ b/js/controllers/home.js @@ -1,12 +1,28 @@ 'use strict'; -angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils) { +angular.module('copayApp.controllers').controller('HomeController', function($scope, $rootScope, $location, notification, controllerUtils, pluginManager) { controllerUtils.redirIfLogged(); - //$scope.retreiving = true; - // identity.getWallets(function(err,ret) { - // $scope.retreiving = false; - // $scope.hasWallets = (ret && ret.length > 0) ? true : false; - // }); + $scope.openProfile = function(form) { + $scope.loading = true; + copay.Identity.open(form.email.$modelValue, form.password.$modelValue, { + pluginManager: pluginManager, + network: config.network, + networkName: config.networkName, + walletDefaults: config.wallet, + passphrase: config.passphrase, + }, function(err, iden, w) { + if (err && !w) { + console.log('Error:' + err) + controllerUtils.onErrorDigest( + $scope, (err.toString()||'').match('PNOTFOUND') ? 'Profile not found' : 'Unknown error'); + } else { + $scope.loading = false; + $rootScope.iden = iden; + $rootScope.wallet = w; + controllerUtils.bindWallet(w, $scope); + } + }); + } }); diff --git a/js/models/Identity.js b/js/models/Identity.js index 05bb82df7..a3865f340 100644 --- a/js/models/Identity.js +++ b/js/models/Identity.js @@ -98,6 +98,13 @@ Identity._walletDelete = function(id, cb) { return Wallet.delete(id, cb); }; +/* for stubbing */ +Identity._openProfile = function(email, password, storage, cb) { + Profile.open(email, password, storage, cb); +}; + + + /** * creates and Identity * @@ -160,10 +167,14 @@ Identity.prototype.validate = function(authcode, cb) { Identity.open = function(email, password, opts, cb) { var iden = new Identity(email, password, opts); - Identity._createProfile(email, password, iden.storage, function(err, profile) { + Identity._openProfile(email, password, iden.storage, function(err, profile) { if (err) return cb(err); iden.profile = profile; - return cb(null, iden); + var wid = iden.listWallets()[0].id; + iden.openWallet(wid, password, function(err, w) { + return cb(err, iden, w); + }) + }); }; @@ -217,15 +228,17 @@ Identity.prototype.store = function(opts, cb) { Identity.prototype.close = function(cb) { preconditions.checkState(this.profile); - var l = self.openWallets.length, + var l = this.openWallets.length, i = 0; - if (!l) return cb(); + if (!l) { + return cb ? cb() : null; + } - _.each(self.openWallets, function(w) { + _.each(this.openWallets, function(w) { w.close(function(err) { if (err) return cb(err); - if (++i == l) + if (++i == l && cb) return cb(); }) }); @@ -390,18 +403,19 @@ Identity.prototype.openWallet = function(walletId, password, cb) { var self = this; self.storage.setPassword(password); - self.migrateWallet(walletId, password, function() { + // TODO + // self.migrateWallet(walletId, password, function() { - Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { - if (err) return cb(err); + Identity._walletRead(walletId, self.storage, self.networks, self.blockchains, [], function(err, w) { + if (err) return cb(err); - w.store(function(err) { - self.profile.setLastOpenedTs(walletId, function() { - return cb(err, w); - }); + w.store(function(err) { + self.profile.setLastOpenedTs(walletId, function() { + return cb(err, w); }); }); }); + // }); }; diff --git a/js/models/Profile.js b/js/models/Profile.js index 69113c5cf..83b810c48 100644 --- a/js/models/Profile.js +++ b/js/models/Profile.js @@ -13,9 +13,9 @@ function Profile(info, storage) { this.hash = info.hash; this.email = info.email; this.extra = info.extra; + this.walletInfos = info.walletInfos || {}; this.key = Profile.key(this.hash); - this.walletInfos = {}; this.storage = storage; }; @@ -36,29 +36,31 @@ Profile.create = function(email, password, storage, cb) { var p = new Profile({ email: email, - hash: Profile.hash(email,password), + hash: Profile.hash(email, password), }, storage); p.store({}, function(err) { - return cb(err,p); + return cb(err, p); }); }; Profile.open = function(email, password, storage, cb) { preconditions.checkArgument(cb); + preconditions.checkState(storage.hasPassphrase()); var key = Profile.key(Profile.hash(email, password)); - storage.getGlobal(key, function(err, val) { - if (err) return cb(err); - - if (!val) + storage.get(key, function(err, val) { + if (err || !val) return cb(new Error('PNOTFOUND: Profile not found')); - return cb(new Profile(val, storage)); + if (!val.email) + return cb(new Error('PERROR: Could not open profile')); + + return cb(null, new Profile(val, storage)); }); }; Profile.prototype.toObj = function() { - return JSON.parse(JSON.stringify(this)); + return _.clone(_.pick(this, 'hash', 'email', 'extra', 'walletInfos')); }; Profile.prototype.getWallet = function(walletId, cb) { @@ -74,7 +76,7 @@ Profile.prototype.listWallets = function(opts, cb) { Profile.prototype.deleteWallet = function(walletId, cb) { if (!this.walletInfos[walletId]) - return cb(new Error('WNOEXIST: Wallet not on profile')); + return cb(new Error('WNOEXIST: Wallet not on profile ')); delete this.walletInfos[walletId]; @@ -85,7 +87,7 @@ Profile.prototype.deleteWallet = function(walletId, cb) { Profile.prototype.addToWallet = function(walletId, info, cb) { if (!this.walletInfos[walletId]) - return cb(new Error('WNOEXIST: Wallet not on profile')); + return cb(new Error('WNOEXIST: Wallet not on profile ')); this.walletInfos[walletId] = _.extend(this.walletInfos[walletId], info); @@ -100,7 +102,7 @@ Profile.prototype.addWallet = function(walletId, info, cb) { preconditions.checkArgument(cb); if (this.walletInfos[walletId]) - return cb(new Error('WEXIST: Wallet already on profile')); + return cb(new Error('WEXIST: Wallet already on profile ')); this.walletInfos[walletId] = _.extend(info, { createdTs: Date.now(), @@ -128,7 +130,7 @@ Profile.prototype.store = function(opts, cb) { if (val2 && !opts.overwrite) { if (cb) - return cb(new Error('PEXISTS: Profile already exist')) + return cb(new Error('PEXISTS: Profile already exist ')) } else { self.storage.set(key, val, function(err) { log.debug('Profile stored'); diff --git a/js/models/Storage.js b/js/models/Storage.js index 9612a7c3d..0c4a4daa6 100644 --- a/js/models/Storage.js +++ b/js/models/Storage.js @@ -89,7 +89,7 @@ Storage.prototype._read = function(k, cb) { var self = this; this.db.getItem(k, function(ret) { if (!ret) return cb(null); - var ret = self._decrypt(ret); + ret = self._decrypt(ret); if (!ret) return cb(null); ret = ret.toString(CryptoJS.enc.Utf8); diff --git a/js/models/Wallet.js b/js/models/Wallet.js index 7c2bdd92a..0b3ad2e82 100644 --- a/js/models/Wallet.js +++ b/js/models/Wallet.js @@ -71,6 +71,7 @@ function Wallet(opts) { self[k] = opts[k]; }); + this.id = opts.id || Wallet.getRandomId(); this.secretNumber = opts.secretNumber || Wallet.getRandomNumber(); this.lock = new WalletLock(this.storage, this.id, opts.lockTimeOutMin); @@ -94,10 +95,12 @@ function Wallet(opts) { this.paymentRequests = opts.paymentRequests || {}; + var networkName = Wallet.obtainNetworkName(opts); + this.network = networkName && this.network[networkName] ? this.network[networkName] : this.network; + this.blockchain = networkName && this.blockchain[networkName] ? this.blockchain[networkName] : this.blockchain; - var networkName = Wallet.obtainNetworkName(this); - this.network = _.isArray(this.network)? this.network[networkName] : this.network; - this.blockchain = _.isArray(this.blockchain) ? this.blockchain[networkName] : this.blockchain; + preconditions.checkArgument(this.network.setHexNonce, 'Incorrect network parameter'); + preconditions.checkArgument(this.blockchain.getTransaction, 'Incorrect blockchain parameter'); this.network.maxPeers = this.totalCopayers; @@ -217,8 +220,9 @@ Wallet.delete = function(walletId, storage, cb) { * @param {function} callback - {err, Wallet} * @return {undefined} */ -Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) { +Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) { preconditions.checkArgument(cb); + preconditions.checkArgument(storage.setPassword); var self = this, err; @@ -237,10 +241,12 @@ Wallet.read = function(walletId, storage, network, blockchain, skipFields, cb) var w, err; obj.id = walletId; try { +console.log('[Wallet.js.218:network:]',network); //TODO w = self.fromObj(obj, storage, network, blockchain, skipFields); } catch (e) { + log.debug("ERROR: ", e.message); if (e && e.message && e.message.indexOf('MISSOPTS')) { - err = new Error('WERROR: Could not read: ' + walletId); + err = new Error('WERROR: Could not read: ' + walletId + ': ' + e.message); } else { err = e; } @@ -1637,6 +1643,7 @@ Wallet.prototype.sendPaymentTx = function(ntxid, options, cb) { options = {}; } +console.log('[Wallet.js.1613:ntxid:]',ntxid); //TODO var txp = this.txProposals.get(ntxid); if (!txp) return; diff --git a/test/PayPro.js b/test/PayPro.js index c155311ed..f80c6c26f 100644 --- a/test/PayPro.js +++ b/test/PayPro.js @@ -83,6 +83,14 @@ describe('PayPro (in Wallet) model', function() { c.networkName = walletConfig.networkName; c.version = '0.0.1'; + c.network = sinon.stub(); + c.network.setHexNonce = sinon.stub(); + c.network.setHexNonces = sinon.stub(); + c.network.getHexNonce = sinon.stub(); + c.network.getHexNonces = sinon.stub(); + c.network.send = sinon.stub(); + + return new Wallet(c); } @@ -683,9 +691,11 @@ describe('PayPro (in Wallet) model', function() { }); }); - it('#add tx proposal based on payment message via model', function(done) { + it('#add tx proposal based on payment message via model ', function(done) { + var w = ppw; should.exist(w); + w.sendPaymentTx(w._ntxid, function(txid, merchantData) { should.exist(txid); should.exist(merchantData); diff --git a/test/Wallet.js b/test/Wallet.js index d4814c31a..0c59ed97b 100644 --- a/test/Wallet.js +++ b/test/Wallet.js @@ -1894,12 +1894,13 @@ describe('Wallet model', function() { var storage = new s(); var network = new Network(walletConfig.network); var blockchain = new Blockchain(walletConfig.blockchain); + storage.setPassword = sinon.stub(); it('should fail to read an unexisting wallet', function(done) { - s.getFirst = sinon.stub().yields(null); + storage.getFirst = sinon.stub().yields(null); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { err.toString().should.contain('WNOTFOUND'); done(); }); @@ -1907,25 +1908,25 @@ describe('Wallet model', function() { it('should not read a corrupted wallet', function(done) { - s.getFirst = sinon.stub().yields(null, '{hola:1}'); + storage.getFirst = sinon.stub().yields(null, '{hola:1}'); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { err.toString().should.contain('WERROR'); done(); }); }); it('should read a wallet', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(o)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + storage.getFirst = sinon.stub().yields(null, JSON.parse(o)); + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.not.exist(err); done(); }); }); it('should be able to import unencrypted legacy wallet TxProposal: v0', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(legacyO)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + storage.getFirst = sinon.stub().yields(null, JSON.parse(legacyO)); + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.exist(w); w.id.should.equal('55d4bd062d32f90a'); should.exist(w.publicKeyRing.getCopayerId); @@ -1936,9 +1937,9 @@ describe('Wallet model', function() { }); it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function(done) { - s.getFirst = sinon.stub().yields(null, JSON.parse(legacy1)); + storage.getFirst = sinon.stub().yields(null, JSON.parse(legacy1)); - Wallet.read('123', s, network, blockchain, [], function(err, w) { + Wallet.read('123', storage, network, blockchain, [], function(err, w) { should.exist(w); w.isReady().should.equal(true); var wo = w.toObj(); diff --git a/test/test.Identity.js b/test/test.Identity.js index 25aabe7ee..eeebacfeb 100644 --- a/test/test.Identity.js +++ b/test/test.Identity.js @@ -33,10 +33,10 @@ describe('Identity model', function() { beforeEach(function(done) { storage = sinon.stub(); storage.getItem = sinon.stub(); - storage.setPassphrase = sinon.spy(); + storage.setPassword = sinon.spy(); + storage.hasPassphrase = sinon.stub().returns(true); storage.getSessionId = sinon.spy(); storage.setFromObj = sinon.spy(); - storage.setLastOpened = sinon.stub().yields(null); storage.deletePrefix = sinon.stub().yields(null); Identity._newStorage = sinon.stub().returns(storage); @@ -127,14 +127,17 @@ describe('Identity model', function() { describe('#open', function(done) { beforeEach(function() { - Identity._createProfile = sinon.stub().callsArgWith(3, null, 'kk'); + storage.getFirst = sinon.stub().yields('wallet1234'); + profile.listWallets = sinon.stub().returns([{id:'walletid'}]); + Identity._openProfile = sinon.stub().callsArgWith(3, null, profile); + Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); }); - it('should call ._createProfile', function(done) { - Identity.open(email, password, config, function(err, iden) { + it('should call ._openProfile', function(done) { + Identity.open(email, password, config, function(err, iden, w) { + Identity._openProfile.calledOnce.should.equal(true); should.not.exist(err); - iden.profile.should.equal('kk'); - Identity._createProfile.calledOnce.should.equal(true); + iden.profile.should.equal(profile); done(); }); }); @@ -154,7 +157,7 @@ describe('Identity model', function() { it('should call .store from profile and wallets (2)', function(done) { iden.profile.store = sinon.stub().yields(null); - iden.wallets = [{ + iden.openWallets = [{ store: sinon.stub().yields(null) }, { store: sinon.stub().yields(null) @@ -162,8 +165,8 @@ describe('Identity model', function() { iden.store({}, function(err) { should.not.exist(err); iden.profile.store.calledOnce.should.equal(true); - iden.wallets[0].store.calledOnce.should.equal(true); - iden.wallets[1].store.calledOnce.should.equal(true); + iden.openWallets[0].store.calledOnce.should.equal(true); + iden.openWallets[1].store.calledOnce.should.equal(true); done(); }); }); @@ -226,8 +229,7 @@ describe('Identity model', function() { beforeEach(function() { iden.migrateWallet = sinon.stub().yields(null); - iden.profile.setLastOpened = sinon.stub().yields(null); - iden.storage.setPassphrase = sinon.spy(); + storage.setPassword = sinon.spy(); storage.getFirst = sinon.stub().yields('wallet1234'); var wallet = sinon.stub(); @@ -236,14 +238,14 @@ describe('Identity model', function() { Identity._walletRead = sinon.stub().callsArgWith(5, null, wallet); }); - it('should call setPassphrase', function(done) { + it('should call setPassword', function(done) { var s1 = sinon.stub(); s1.store = sinon.stub().yields(null); iden.openWallet('id123', 'xxx', function(err, w) { - iden.storage.setPassphrase.calledOnce.should.equal(true); - iden.storage.setPassphrase.getCall(0).args[0].should.equal('xxx'); + iden.storage.setPassword.calledOnce.should.equal(true); + iden.storage.setPassword.getCall(0).args[0].should.equal('xxx'); done(); }); }); @@ -254,7 +256,7 @@ describe('Identity model', function() { should.not.exist(err); w.store.calledOnce.should.equal(true); iden.profile.setLastOpenedTs.calledTwice.should.equal(true); - iden.migrateWallet.calledOnce.should.equal(true); + // iden.migrateWallet.calledOnce.should.equal(true); done(); }); }); @@ -332,7 +334,7 @@ describe('Identity model', function() { var opts = { secret: '8WtTuiFTkhP5ao7AF2QErSwV39Cbur6pdMebKzQXFqL59RscXM', nickname: 'test', - passphrase: 'pass' + password: 'pass' }; it('should yield bad network error', function(done) { diff --git a/test/test.Profile.js b/test/test.Profile.js index e7f90dd9b..3e2c87639 100644 --- a/test/test.Profile.js +++ b/test/test.Profile.js @@ -19,7 +19,7 @@ describe('Profile model', function() { }; beforeEach(function() { - storage.setPassphrase = sinon.stub(); + storage.setPassword = sinon.stub(); storage.set = sinon.stub(); storage.set.yields(null); storage.get = sinon.stub().yields(null); diff --git a/views/home.html b/views/home.html index 3570e6b9d..44ab7b0aa 100644 --- a/views/home.html +++ b/views/home.html @@ -1,7 +1,8 @@
-

( TODO1: only this form if there is any profile:: key) -

( TODO2: if user has wallets (wallet::) show message: Copay now needs a profile to ... , you can import your wallets after creating your profile ) +

( TODO: only show this login form if there is any profile:: key) + +

( TODO: if user has wallets (wallet::) show message: Copay now needs a profile to ... , you can import your wallets after creating your profile )

Retreiving information from storage... @@ -14,12 +15,12 @@

Login

-
+
- + - +