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 145 additions and 98 deletions

View File

@ -1244,6 +1244,21 @@ label.postfix, span.postfix {
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 {
background: #34495E;
color: #fff;

View File

@ -18,71 +18,72 @@ angular.module('copayApp.controllers').controller('ImportController',
$scope.$digest();
}
$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
var encryptedObj = evt.target.result;
$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
var encryptedObj = evt.target.result;
updateStatus('Importing wallet - Procesing backup...');
identityService.importWallet(encryptedObj, $scope.password, {}, function(err) {
if (err) {
$scope.loading = false;
$scope.error = 'Could not read wallet. Please check your password';
}
});
}
}
};
$scope.import = function(form) {
$scope.loading = true;
if (form.$invalid) {
$scope.loading = false;
$scope.error = 'There is an error in the form';
return;
}
var backupFile = $scope.file;
var backupText = form.backupText.$modelValue;
var backupOldWallet = form.backupOldWallet.$modelValue;
var password = form.password.$modelValue;
if (backupOldWallet) {
backupText = backupOldWallet.value;
}
if (!backupFile && !backupText) {
$scope.loading = false;
$scope.error = 'Please, select your backup file';
return;
}
$scope.importOpts = {};
var skipFields = [];
if ($scope.skipPublicKeyRing)
skipFields.push('publicKeyRing');
if ($scope.skipTxProposals)
skipFields.push('txProposals');
if (skipFields)
$scope.importOpts.skipFields = skipFields;
if (backupFile) {
reader.readAsBinaryString(backupFile);
} else {
updateStatus('Importing wallet - Procesing backup...');
identityService.importWallet(encryptedObj, $scope.password, {}, function(err){
identityService.importWallet(encryptedObj, $scope.password, $scope.importOpts, function(err) {
if (err) {
$scope.loading = false;
$scope.error = 'Could not read wallet. Please check your password';
}
copay.Compatibility.deleteOldWallet(backupOldWallet);
});
}
};
};
$scope.import = function(form) {
$scope.loading = true;
if (form.$invalid) {
$scope.loading = false;
$scope.error = 'There is an error in the form';
return;
}
var backupFile = $scope.file;
var backupText = form.backupText.$modelValue;
var backupOldWallet = form.backupOldWallet.$modelValue;
var password = form.password.$modelValue;
if (backupOldWallet) {
backupText = backupOldWallet.value;
}
if (!backupFile && !backupText) {
$scope.loading = false;
$scope.error = 'Please, select your backup file';
return;
}
$scope.importOpts = {};
var skipFields = [];
if ($scope.skipPublicKeyRing)
skipFields.push('publicKeyRing');
if ($scope.skipTxProposals)
skipFields.push('txProposals');
if (skipFields)
$scope.importOpts.skipFields = skipFields;
if (backupFile) {
reader.readAsBinaryString(backupFile);
} else {
updateStatus('Importing wallet - Procesing backup...');
identityService.importWallet(encryptedObj, $scope.password, $scope.importOpts, function(err){
if (err) {
$scope.loading = false;
$scope.error = 'Could not read wallet. Please check your password';
}
copay.Compatibility.deleteOldWallet(backupOldWallet);
});
}
};
});
});

View File

@ -14,7 +14,7 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
$scope.backupProfilePlainText = backupService.profileEncrypted($rootScope.iden);
$scope.hideViewProfileBackup = true;
};
$scope.deleteWallet = function(w) {
if (!w) return;
identityService.deleteWallet(w, function(err) {
@ -28,8 +28,8 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
$scope.init = function() {
if ($rootScope.quotaPerItem) {
$scope.perItem = $filter('noFractionNumber')($rootScope.quotaPerItem/1000,1);
$scope.nrWallets =parseInt($rootScope.quotaItems) - 1;
$scope.perItem = $filter('noFractionNumber')($rootScope.quotaPerItem / 1000, 1);
$scope.nrWallets = parseInt($rootScope.quotaItems) - 1;
}
};
@ -37,13 +37,13 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
if (!$rootScope.iden) return;
var wallets = $rootScope.iden.listWallets();
var max =$rootScope.quotaPerItem;
var max = $rootScope.quotaPerItem;
_.each(wallets, function(w) {
var bits = w.sizes().total;
w.kb = $filter('noFractionNumber')(bits/1000, 1);
w.kb = $filter('noFractionNumber')(bits / 1000, 1);
if (max) {
w.usage = $filter('noFractionNumber')(bits/max * 100, 0);
w.usage = $filter('noFractionNumber')(bits / max * 100, 0);
}
});
@ -73,15 +73,15 @@ angular.module('copayApp.controllers').controller('ProfileController', function(
});
};
$scope.deleteProfile = function () {
identityService.deleteProfile(function (err, res) {
$scope.deleteProfile = function() {
identityService.deleteProfile(function(err, res) {
if (err) {
log.warn(err);
notification.error('Error', 'Could not delete profile');
return;
}
$location.path('/');
setTimeout(function () {
$location.path('/');
setTimeout(function() {
notification.error('Success', 'Profile successfully deleted');
}, 1);
});

View File

@ -62,6 +62,8 @@ function Identity(opts) {
this.walletIds = opts.walletIds || {};
this.wallets = opts.wallets || {};
this.focusedTimestamps = opts.focusedTimestamps || {};
this.backupNeeded = opts.backupNeeded || false;
};
@ -91,7 +93,9 @@ Identity.prototype.getName = function() {
* @return {undefined}
*/
Identity.create = function(opts, cb) {
opts = _.extend({}, opts);
opts = _.extend({
backupNeeded: true
}, opts);
var iden = new Identity(opts);
iden.store(_.extend(opts, {
@ -265,21 +269,36 @@ Identity.prototype.toObj = function() {
return _.extend({
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) {
var crypto = opts.cryptoUtil || cryptoUtil;
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) {
return _.extend({
wallets: _.map(this.wallets, function(wallet) {
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
*/
Identity.prototype.store = function(opts, cb) {
log.debug('Storing profile');
var self = this;
opts = opts || {};
var storeFunction = opts.failIfExists ? self.storage.createItem : self.storage.setItem;
storeFunction.call(self.storage, this.getId(), this.toObj(), function(err) {
if (err) return cb(err);
if (err) {
return cb(err);
}
if (opts.noWallets)
return cb();
@ -323,9 +342,9 @@ Identity.prototype.remove = function(opts, cb) {
if (err) return cb(err);
cb();
});
}, function (err) {
}, function(err) {
if (err) return cb(err);
self.storage.removeItem(self.getId(), function(err) {
if (err) return cb(err);
self.emitAndKeepAlive('closed');
@ -552,13 +571,16 @@ Identity.prototype.createWallet = function(opts, cb) {
var self = this;
var w = new walletClass(opts);
self.bindWallet(w);
self.updateFocusedTimestamp(w.getId());
self.storeWallet(w, function(err) {
if (err) return cb(err);
self.backupNeeded = true;
self.store({
noWallets: true
noWallets: true,
}, function(err) {
return cb(err, w);
});

View File

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

View File

@ -48,6 +48,7 @@ angular.module('copayApp.services')
passphraseConfig: config.passphraseConfig,
failIfExists: true,
}, function(err, iden) {
if (err) return cb(err);
preconditions.checkState(iden);
root.bind(iden);
@ -68,19 +69,19 @@ angular.module('copayApp.services')
};
root.setServerStatus = function(headers) {
if (!headers)
if (!headers)
return;
if (headers['X-Email-Needs-Validation'])
$rootScope.needsEmailConfirmation = true;
if (headers['X-Email-Needs-Validation'])
$rootScope.needsEmailConfirmation = true;
else
$rootScope.needsEmailConfirmation = null;
$rootScope.needsEmailConfirmation = null;
if (headers['X-Quota-Per-Item'])
$rootScope.quotaPerItem = parseInt(headers['X-Quota-Per-Item']);
if (headers['X-Quota-Per-Item'])
$rootScope.quotaPerItem = parseInt(headers['X-Quota-Per-Item']);
if (headers['X-Quota-Items-Limit'])
$rootScope.quotaItems = parseInt(headers['X-Quota-Items-Limit']);
if (headers['X-Quota-Items-Limit'])
$rootScope.quotaItems = parseInt(headers['X-Quota-Items-Limit']);
};
root.open = function(email, password, cb) {
@ -102,7 +103,7 @@ angular.module('copayApp.services')
});
};
root.deleteProfile = function (cb) {
root.deleteProfile = function(cb) {
$rootScope.iden.remove(null, cb);
};

View File

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

View File

@ -76,6 +76,7 @@ describe("Angular services", function() {
a[Waddr] = 200;
w.getBalance = sinon.stub().yields(null, 100000001, a, 90000002, 5);
//retuns values in DEFAULT UNIT(bits)
balanceService.update(w, function() {
var b = w.balanceInfo;
@ -90,7 +91,7 @@ describe("Angular services", function() {
expect(b.balanceByAddr[Waddr]).to.equal(2);
expect(b.safeUnspentCount).to.equal(5);
expect(b.topAmount).to.equal(899800.02);
},false);
}, false);
}));
});

View File

@ -16,10 +16,13 @@
<div class="menu" ng-mouseover="hoverIn()" ng-mouseleave="hoverOut()"
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">
<img gravatar-src="'{{username}}'" gravatar-size="35">
</div>
<span class="need-backup" ng-if="!$root.needsEmailConfirmation && $root.iden.backupNeeded">
<i class="fi-alert vm"></i>
</span>
<span class="m15t">{{username}} </span>
<i class="icon-arrow-down2 size-16 vm"></i>
</a>
@ -33,7 +36,8 @@
<i class="icon-download size-18 m10r"></i> {{'Import wallet'|translate}}</a></li>
<li class="divider"></li>
<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()">
<i class="icon-power size-18 m10r"></i> {{'Close'|translate}}</a></li>
</ul>

View File

@ -15,7 +15,7 @@
</a>
</div>
<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>
</div>
<div class="large-3 medium-3 columns">