Merge pull request #1850 from matiaspando/feature/backupFlag

Added the flag backupNeeded
This commit is contained in:
Matias Alejo Garcia 2014-12-02 11:31:39 -03:00
commit 3829d6692f
10 changed files with 139 additions and 92 deletions

View file

@ -1244,6 +1244,21 @@ label.postfix, span.postfix {
height: 80px; height: 80px;
} }
.need-backup {
background: #C0392A;
-moz-box-shadow: 1px 1px 0px 0px #A02F23;
box-shadow: 1px 1px 0px 0px #A02F23;
position: absolute;
top: 22px;
left: 0px;
width: 14px;
height: 14px;
border-radius: 100%;
font-size: 9px;
padding-top: 2px;
color: #fff;
}
a:hover .photo-container { a:hover .photo-container {
background: #34495E; background: #34495E;
color: #fff; color: #fff;

View file

@ -18,6 +18,7 @@ angular.module('copayApp.controllers').controller('ImportController',
$scope.$digest(); $scope.$digest();
} }
$scope.getFile = function() { $scope.getFile = function() {
// If we use onloadend, we need to check the readyState. // If we use onloadend, we need to check the readyState.
reader.onloadend = function(evt) { reader.onloadend = function(evt) {
@ -31,7 +32,7 @@ angular.module('copayApp.controllers').controller('ImportController',
} }
}); });
} }
}; }
}; };
$scope.import = function(form) { $scope.import = function(form) {

View file

@ -62,6 +62,8 @@ function Identity(opts) {
this.walletIds = opts.walletIds || {}; this.walletIds = opts.walletIds || {};
this.wallets = opts.wallets || {}; this.wallets = opts.wallets || {};
this.focusedTimestamps = opts.focusedTimestamps || {}; this.focusedTimestamps = opts.focusedTimestamps || {};
this.backupNeeded = opts.backupNeeded || false;
}; };
@ -91,7 +93,9 @@ Identity.prototype.getName = function() {
* @return {undefined} * @return {undefined}
*/ */
Identity.create = function(opts, cb) { Identity.create = function(opts, cb) {
opts = _.extend({}, opts); opts = _.extend({
backupNeeded: true
}, opts);
var iden = new Identity(opts); var iden = new Identity(opts);
iden.store(_.extend(opts, { iden.store(_.extend(opts, {
@ -265,21 +269,36 @@ Identity.prototype.toObj = function() {
return _.extend({ return _.extend({
walletIds: _.isEmpty(this.wallets) ? this.walletsIds : _.keys(this.wallets), walletIds: _.isEmpty(this.wallets) ? this.walletsIds : _.keys(this.wallets),
}, },
_.pick(this, 'version', 'fullName', 'password', 'email', 'focusedTimestamps')); _.pick(this, 'version', 'fullName', 'password', 'email', 'backupNeeded', 'focusedTimestamps'));
}; };
Identity.prototype.exportEncryptedWithWalletInfo = function(opts) { Identity.prototype.exportEncryptedWithWalletInfo = function(opts) {
var crypto = opts.cryptoUtil || cryptoUtil; var crypto = opts.cryptoUtil || cryptoUtil;
return crypto.encrypt(this.password, this.exportWithWalletInfo(opts)); return crypto.encrypt(this.password, this.exportWithWalletInfo(opts));
}; };
Identity.prototype.setBackupNeeded = function() {
this.backupNeeded = true;
this.store({
noWallets: true
}, function() {});
}
Identity.prototype.setBackupDone = function() {
this.backupNeeded = false;
this.store({
noWallets: true
}, function() {});
}
Identity.prototype.exportWithWalletInfo = function(opts) { Identity.prototype.exportWithWalletInfo = function(opts) {
return _.extend({ return _.extend({
wallets: _.map(this.wallets, function(wallet) { wallets: _.map(this.wallets, function(wallet) {
return wallet.toObj(); return wallet.toObj();
}) })
}, },
_.pick(this, 'version', 'fullName', 'password', 'email') _.pick(this, 'version', 'fullName', 'password', 'email', 'backupNeeded')
); );
}; };
@ -288,15 +307,15 @@ Identity.prototype.exportWithWalletInfo = function(opts) {
* @param {Function} cb * @param {Function} cb
*/ */
Identity.prototype.store = function(opts, cb) { Identity.prototype.store = function(opts, cb) {
log.debug('Storing profile');
var self = this; var self = this;
opts = opts || {}; opts = opts || {};
var storeFunction = opts.failIfExists ? self.storage.createItem : self.storage.setItem; var storeFunction = opts.failIfExists ? self.storage.createItem : self.storage.setItem;
storeFunction.call(self.storage, this.getId(), this.toObj(), function(err) { storeFunction.call(self.storage, this.getId(), this.toObj(), function(err) {
if (err) return cb(err); if (err) {
return cb(err);
}
if (opts.noWallets) if (opts.noWallets)
return cb(); return cb();
@ -552,13 +571,16 @@ Identity.prototype.createWallet = function(opts, cb) {
var self = this; var self = this;
var w = new walletClass(opts); var w = new walletClass(opts);
self.bindWallet(w); self.bindWallet(w);
self.updateFocusedTimestamp(w.getId()); self.updateFocusedTimestamp(w.getId());
self.storeWallet(w, function(err) { self.storeWallet(w, function(err) {
if (err) return cb(err); if (err) return cb(err);
self.backupNeeded = true;
self.store({ self.store({
noWallets: true noWallets: true,
}, function(err) { }, function(err) {
return cb(err, w); return cb(err, w);
}); });

View file

@ -38,6 +38,7 @@ BackupService.prototype.profileEncrypted = function(iden) {
BackupService.prototype.profileDownload = function(iden) { BackupService.prototype.profileDownload = function(iden) {
var ew = this.profileEncrypted(iden); var ew = this.profileEncrypted(iden);
iden.setBackupDone();
var name = iden.fullName; var name = iden.fullName;
var filename = name + '-profile.json'; var filename = name + '-profile.json';
this._download(ew, name, filename) this._download(ew, name, filename)

View file

@ -48,6 +48,7 @@ angular.module('copayApp.services')
passphraseConfig: config.passphraseConfig, passphraseConfig: config.passphraseConfig,
failIfExists: true, failIfExists: true,
}, function(err, iden) { }, function(err, iden) {
if (err) return cb(err); if (err) return cb(err);
preconditions.checkState(iden); preconditions.checkState(iden);
root.bind(iden); root.bind(iden);

View file

@ -111,7 +111,14 @@ describe('Identity model', function() {
params: params params: params
}; };
}; };
var orig;
beforeEach(function() {
orig = Identity.prototype.store;
sinon.stub(Identity.prototype, 'store').yields(null);
});
afterEach(function() {
Identity.prototype.store = orig;
});
describe('new Identity()', function() { describe('new Identity()', function() {
it('returns an identity', function() { it('returns an identity', function() {
var iden = new Identity(getDefaultParams()); var iden = new Identity(getDefaultParams());
@ -124,7 +131,6 @@ describe('Identity model', function() {
it('should create and store identity', function() { it('should create and store identity', function() {
var args = createIdentity(); var args = createIdentity();
args.blockchain.on = sinon.stub(); args.blockchain.on = sinon.stub();
sinon.stub(Identity.prototype, 'store').yields(null);
Identity.create(args.params, function(err, iden) { Identity.create(args.params, function(err, iden) {
should.not.exist(err); should.not.exist(err);
should.exist(iden); should.exist(iden);
@ -240,7 +246,6 @@ describe('Identity model', function() {
args = createIdentity(); args = createIdentity();
args.params.noWallets = true; args.params.noWallets = true;
var old = Identity.prototype.createWallet; var old = Identity.prototype.createWallet;
sinon.stub(Identity.prototype, 'store').yields(null);
Identity.create(args.params, function(err, res) { Identity.create(args.params, function(err, res) {
iden = res; iden = res;
}); });
@ -297,7 +302,6 @@ describe('Identity model', function() {
args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}'); args.storage.getItem.onFirstCall().callsArgWith(1, null, '{"wallet": "fakeData"}');
var backup = Wallet.fromUntrustedObj; var backup = Wallet.fromUntrustedObj;
args.params.noWallets = true; args.params.noWallets = true;
sinon.stub(Identity.prototype, 'store').yields(null);
sinon.stub().returns(args.wallet); sinon.stub().returns(args.wallet);
var opts = { var opts = {
@ -390,8 +394,6 @@ describe('Identity model', function() {
beforeEach(function() { beforeEach(function() {
args = createIdentity(); args = createIdentity();
args.params.Async = net = sinon.stub(); args.params.Async = net = sinon.stub();
sinon.stub(Identity.prototype, 'store').yields(null);
net.cleanUp = sinon.spy(); net.cleanUp = sinon.spy();
net.on = sinon.stub(); net.on = sinon.stub();
net.start = sinon.spy(); net.start = sinon.spy();

View file

@ -76,6 +76,7 @@ describe("Angular services", function() {
a[Waddr] = 200; a[Waddr] = 200;
w.getBalance = sinon.stub().yields(null, 100000001, a, 90000002, 5); w.getBalance = sinon.stub().yields(null, 100000001, a, 90000002, 5);
//retuns values in DEFAULT UNIT(bits) //retuns values in DEFAULT UNIT(bits)
balanceService.update(w, function() { balanceService.update(w, function() {
var b = w.balanceInfo; var b = w.balanceInfo;

View file

@ -16,10 +16,13 @@
<div class="menu" ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()" <div class="menu" ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()"
ng-click="hoverMenu = !hoverMenu"> ng-click="hoverMenu = !hoverMenu">
<a class="dropdown ellipsis text-gray" ng-class="{'hover': hoverMenu}"> <a class="dropdown ellipsis text-gray pr" ng-class="{'hover': hoverMenu}">
<div class="photo-container"> <div class="photo-container">
<img gravatar-src="'{{username}}'" gravatar-size="35"> <img gravatar-src="'{{username}}'" gravatar-size="35">
</div> </div>
<span class="need-backup" ng-if="!$root.needsEmailConfirmation && $root.iden.backupNeeded">
<i class="fi-alert vm"></i>
</span>
<span class="m15t">{{username}} </span> <span class="m15t">{{username}} </span>
<i class="icon-arrow-down2 size-16 vm"></i> <i class="icon-arrow-down2 size-16 vm"></i>
</a> </a>
@ -33,7 +36,8 @@
<i class="icon-download size-18 m10r"></i> {{'Import wallet'|translate}}</a></li> <i class="icon-download size-18 m10r"></i> {{'Import wallet'|translate}}</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#!/profile" title="Profile"> <li><a href="#!/profile" title="Profile">
<i class="icon-person size-18 m10r"></i> {{'Profile'|translate}}</a></li> <i class="icon-person size-18 m10r"></i> {{'Profile'|translate}}<span class="size-10 text-warning" ng-if="!$root.needsEmailConfirmation && $root.iden.backupNeeded"> [ Needs Backup ]</span></a>
</li>
<li><a href="#!/" title="Close" ng-click="signout()"> <li><a href="#!/" title="Close" ng-click="signout()">
<i class="icon-power size-18 m10r"></i> {{'Close'|translate}}</a></li> <i class="icon-power size-18 m10r"></i> {{'Close'|translate}}</a></li>
</ul> </ul>

View file

@ -15,7 +15,7 @@
</a> </a>
</div> </div>
<div class="large-7 medium-7 columns"> <div class="large-7 medium-7 columns">
<h2>Backup Profile</h2> <h2>Profile <span class="size-12 text-warning" ng-if="$root.iden.backupNeeded"> [ Needs Backup ]</span></h2>
<p translate class="text-gray">It's important to backup your profile so that you can recover it in case of disaster. The backup will include all your profile's wallets</p> <p translate class="text-gray">It's important to backup your profile so that you can recover it in case of disaster. The backup will include all your profile's wallets</p>
</div> </div>
<div class="large-3 medium-3 columns"> <div class="large-3 medium-3 columns">