fix #50: notification on password copy

This commit is contained in:
Antelle 2016-01-11 20:56:52 +03:00
parent 3b4153ba8a
commit 44c5516041
6 changed files with 81 additions and 45 deletions

View File

@ -51,6 +51,7 @@ var CopyPaste = {
}).bind(null, Launcher.getClipboardText()), clipboardSeconds * 1000);
}, 0);
}
return clipboardSeconds;
}
}
};

View File

@ -1,7 +1,8 @@
'use strict';
var Timeouts = {
AutoSync: 30 * 1000 * 60
AutoSync: 30 * 1000 * 60,
CopyTip: 1500
};
module.exports = Timeouts;

View File

@ -127,6 +127,8 @@ var Locale = {
detDelFromTrash: 'Delete from trash?',
detDelFromTrashBody: 'You will not be able to put it back.',
detDelFromTrashBodyHint: 'To quickly remove all items from trash, click empty icon in Trash menu.',
detPassCopied: 'Password copied',
detPassCopiedTime: 'Password copied for {} seconds',
appSecWarn: 'Not Secure!',
appSecWarnBody1: 'You have loaded this app with insecure connection. ' +

View File

@ -1,12 +1,12 @@
'use strict';
var Tip = function(el) {
var Tip = function(el, config) {
this.el = el;
this.title = el.attr('title');
this.title = config && config.title || el.attr('title');
this.placement = config && config.placement || el.attr('tip-placement');
this.tipEl = null;
this.showTimeout = null;
this.hideTimeout = null;
this.init();
};
Tip.prototype.init = function() {
@ -14,6 +14,49 @@ Tip.prototype.init = function() {
this.el.mouseenter(this.mouseenter.bind(this)).mouseleave(this.mouseleave.bind(this));
};
Tip.prototype.show = function() {
if (this.tipEl) {
this.tipEl.remove();
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
this.hideTimeout = null;
}
}
var tipEl = this.tipEl = $('<div></div>').addClass('tip').appendTo('body').html(this.title);
var rect = this.el[0].getBoundingClientRect(),
tipRect = this.tipEl[0].getBoundingClientRect();
var placement = this.placement || this.getAutoPlacement(rect, tipRect);
tipEl.addClass('tip--' + placement);
var top, left;
var offset = 10;
switch (placement) {
case 'top':
top = rect.top - tipRect.height - offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'bottom':
top = rect.bottom + offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'left':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.left - tipRect.width - offset;
break;
case 'right':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.right + offset;
break;
}
tipEl.css({ top: top, left: left });
};
Tip.prototype.hide = function() {
if (this.tipEl) {
this.tipEl.remove();
this.tipEl = null;
}
};
Tip.prototype.mouseenter = function() {
var that = this;
if (this.showTimeout) {
@ -21,39 +64,7 @@ Tip.prototype.mouseenter = function() {
}
this.showTimeout = setTimeout(function() {
that.showTimeout = null;
if (that.tipEl) {
that.tipEl.remove();
if (that.hideTimeout) {
clearTimeout(that.hideTimeout);
that.hideTimeout = null;
}
}
var tipEl = that.tipEl = $('<div></div>').addClass('tip').appendTo('body').html(that.title);
var rect = that.el[0].getBoundingClientRect(),
tipRect = that.tipEl[0].getBoundingClientRect();
var placement = that.el.attr('tip-placement') || that.getAutoPlacement(rect, tipRect);
tipEl.addClass('tip--' + placement);
var top, left;
var offset = 10;
switch (placement) {
case 'top':
top = rect.top - tipRect.height - offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'bottom':
top = rect.bottom + offset;
left = rect.left + rect.width / 2 - tipRect.width / 2;
break;
case 'left':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.left - tipRect.width - offset;
break;
case 'right':
top = rect.top + rect.height / 2 - tipRect.height / 2;
left = rect.right + offset;
break;
}
tipEl.css({ top: top, left: left });
that.show();
}, 200);
};
@ -63,10 +74,7 @@ Tip.prototype.mouseleave = function() {
that.tipEl.addClass('tip--hide');
this.hideTimeout = setTimeout(function () {
that.hideTimeout = null;
if (that.tipEl) {
that.tipEl.remove();
that.tipEl = null;
}
that.hide();
}, 500);
}
if (this.showTimeout) {
@ -104,7 +112,9 @@ Tip.prototype.getAutoPlacement = function(rect, tipRect) {
Tip.createTips = function(container) {
container.find('[title]').each(function(ix, el) {
if (!el._tip) {
el._tip = new Tip($(el));
var tip = new Tip($(el));
tip.init();
el._tip = tip;
}
});
};

View File

@ -20,6 +20,7 @@ var Backbone = require('backbone'),
Format = require('../../util/format'),
Locale = require('../../util/locale'),
Tip = require('../../util/tip'),
Timeouts = require('../../const/timeouts'),
FileSaver = require('filesaver'),
baron = require('baron'),
kdbxweb = require('kdbxweb');
@ -31,6 +32,8 @@ var DetailsView = Backbone.View.extend({
fieldViews: null,
views: null,
passEditView: null,
passCopyTip: null,
events: {
'click .details__colors-popup-item': 'selectColor',
@ -67,6 +70,10 @@ var DetailsView = Backbone.View.extend({
removeFieldViews: function() {
this.fieldViews.forEach(function(fieldView) { fieldView.remove(); });
this.fieldViews = [];
if (this.passCopyTip) {
this.passCopyTip.hide();
this.passCopyTip = null;
}
},
render: function () {
@ -111,8 +118,9 @@ var DetailsView = Backbone.View.extend({
var model = this.model;
this.fieldViews.push(new FieldViewText({ model: { name: '$UserName', title: Locale.detUser,
value: function() { return model.user; } } }));
this.fieldViews.push(new FieldViewText({ model: { name: '$Password', title: Locale.detPassword, canGen: true,
value: function() { return model.password; } } }));
this.passEditView = new FieldViewText({ model: { name: '$Password', title: Locale.detPassword, canGen: true,
value: function() { return model.password; } } });
this.fieldViews.push(this.passEditView);
this.fieldViews.push(new FieldViewUrl({ model: { name: '$URL', title: Locale.detWebsite,
value: function() { return model.url; } } }));
this.fieldViews.push(new FieldViewText({ model: { name: '$Notes', title: Locale.detNotes, multiline: 'true',
@ -273,7 +281,20 @@ var DetailsView = Backbone.View.extend({
var pw = this.model.password;
var password = pw.getText ? pw.getText() : pw;
CopyPaste.createHiddenInput(password);
CopyPaste.copied();
var clipboardTime = CopyPaste.copied();
if (!this.passCopyTip) {
var passLabel = this.passEditView.labelEl;
var msg = clipboardTime ? Locale.detPassCopiedTime.replace('{}', clipboardTime)
: Locale.detPassCopied;
var tip = new Tip(passLabel, { title: msg, placement: 'right' });
this.passCopyTip = tip;
tip.show();
var that = this;
setTimeout(function() {
tip.hide();
that.passCopyTip = null;
}, Timeouts.CopyTip);
}
}
},

View File

@ -4,6 +4,7 @@ Release notes
Improvements
`+` more reliable binaries management
`+` string resources globalization
`+` #50: notification on password copy
`-` #74: select all in search field
##### v0.5.1 (2015-12-15)