mirror of https://github.com/keeweb/keeweb.git
lock flow
This commit is contained in:
parent
e779d1bcc7
commit
ae29852b60
|
@ -12,15 +12,15 @@ var Alerts = {
|
|||
alert: function(config) {
|
||||
var view = new ModalView({ model: config });
|
||||
view.render();
|
||||
view.on('result', function(res) {
|
||||
view.on('result', function(res, check) {
|
||||
if (res && config.success) {
|
||||
config.success(res);
|
||||
config.success(res, check);
|
||||
}
|
||||
if (!res && config.cancel) {
|
||||
config.cancel();
|
||||
}
|
||||
if (config.complete) {
|
||||
config.complete(res);
|
||||
config.complete(res, check);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -84,6 +84,7 @@ var AppModel = Backbone.Model.extend({
|
|||
},
|
||||
|
||||
closeAllFiles: function() {
|
||||
this.files.each(function(file) { file.close(); });
|
||||
this.files.reset();
|
||||
this.menu.groupsSection.removeAllItems();
|
||||
this.menu.tagsSection.set('scrollable', false);
|
||||
|
|
|
@ -13,7 +13,8 @@ var AppSettingsModel = Backbone.Model.extend({
|
|||
menuViewWidth: null,
|
||||
tagsViewHeight: null,
|
||||
autoUpdate: 'install',
|
||||
clipboradSeconds: 0
|
||||
clipboardSeconds: 0,
|
||||
autoSave: false
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
|
|
|
@ -34,6 +34,7 @@ var FileModel = Backbone.Model.extend({
|
|||
},
|
||||
|
||||
db: null,
|
||||
data: null,
|
||||
|
||||
initialize: function() {
|
||||
},
|
||||
|
@ -80,6 +81,7 @@ var FileModel = Backbone.Model.extend({
|
|||
|
||||
postOpen: function(fileData) {
|
||||
var that = this;
|
||||
this.data = fileData;
|
||||
if (!this.get('offline')) {
|
||||
if (this.get('availOffline')) {
|
||||
Storage.cache.save(this.get('name'), fileData, function (err) {
|
||||
|
@ -158,6 +160,22 @@ var FileModel = Backbone.Model.extend({
|
|||
}, this);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this.set({
|
||||
keyFileName: '',
|
||||
passwordLength: 0,
|
||||
modified: false,
|
||||
open: false,
|
||||
opening: false,
|
||||
error: false,
|
||||
created: false,
|
||||
groups: null,
|
||||
passwordChanged: false,
|
||||
keyFileChanged: false,
|
||||
syncing: false
|
||||
});
|
||||
},
|
||||
|
||||
getGroup: function(id) {
|
||||
var found = null;
|
||||
if (id) {
|
||||
|
@ -208,7 +226,7 @@ var FileModel = Backbone.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
autoSave: function() {
|
||||
autoSave: function(complete) {
|
||||
var that = this;
|
||||
that.set('syncing', true);
|
||||
switch (that.get('storage')) {
|
||||
|
@ -216,14 +234,18 @@ var FileModel = Backbone.Model.extend({
|
|||
that.getData(function(data) {
|
||||
Launcher.writeFile(that.get('path'), data);
|
||||
that.saved(that.get('path'), that.get('storage'));
|
||||
if (complete) { complete(); }
|
||||
});
|
||||
break;
|
||||
case 'dropbox':
|
||||
that.getData(function(data) {
|
||||
DropboxLink.saveFile(that.get('path'), data, true, function (err) {
|
||||
if (!err) {
|
||||
if (err) {
|
||||
that.set('syncing', false);
|
||||
} else {
|
||||
that.saved(that.get('path'), that.get('storage'));
|
||||
}
|
||||
if (complete) { complete(err); }
|
||||
});
|
||||
});
|
||||
break;
|
||||
|
@ -233,9 +255,8 @@ var FileModel = Backbone.Model.extend({
|
|||
},
|
||||
|
||||
getData: function(cb) {
|
||||
var data = this.db.save(cb);
|
||||
|
||||
return data;
|
||||
this.data = this.db.save(cb);
|
||||
return this.data;
|
||||
},
|
||||
|
||||
getXml: function(cb) {
|
||||
|
|
|
@ -251,14 +251,87 @@ var AppView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
lockWorkspace: function() {
|
||||
var that = this;
|
||||
if (this.model.files.hasUnsavedFiles()) {
|
||||
Alerts.yesno({
|
||||
header: 'Unsaved changes',
|
||||
body: 'You have unsaved changes that will be lost. Continue?',
|
||||
success: this.model.closeAllFiles.bind(this.model)
|
||||
});
|
||||
if (this.model.settings.get('autoSave')) {
|
||||
this.saveAndLock();
|
||||
} else {
|
||||
Alerts.alert({
|
||||
icon: 'lock',
|
||||
header: 'Lock',
|
||||
body: 'You have unsaved changes that will be lost. Continue?',
|
||||
buttons: [
|
||||
{ result: 'save', title: 'Save changes' },
|
||||
{ result: 'discard', title: 'Discard changes', error: true },
|
||||
{ result: '', title: 'Cancel' }
|
||||
],
|
||||
checkbox: 'Auto save changes each time I lock the app',
|
||||
success: function(result, autoSaveChecked) {
|
||||
if (result === 'save') {
|
||||
if (autoSaveChecked) {
|
||||
that.model.settings.set('autoSave', autoSaveChecked);
|
||||
}
|
||||
that.saveAndLock();
|
||||
} else if (result === 'discard') {
|
||||
that.model.closeAllFiles();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.model.closeAllFiles();
|
||||
this.closeAllFilesAndShowFirst();
|
||||
}
|
||||
},
|
||||
|
||||
saveAndLock: function() {
|
||||
var pendingCallbacks = 0,
|
||||
errorFiles = [],
|
||||
that = this;
|
||||
if (this.model.files.some(function(file) { return file.get('modified') && !file.get('path'); })) {
|
||||
Alerts.error({
|
||||
header: 'Cannot auto-save',
|
||||
body: 'Some opened files cannot be saved automatically. To enable auto-save, you can sync them to Dropbox.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.model.files.forEach(function(file) {
|
||||
if (!file.get('modified')) {
|
||||
return;
|
||||
}
|
||||
if (file.get('path')) {
|
||||
try {
|
||||
file.autoSave(fileSaved.bind(this, file));
|
||||
pendingCallbacks++;
|
||||
} catch (e) {
|
||||
console.error('Failed to auto-save file', file.get('path'), e);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
if (!pendingCallbacks) {
|
||||
this.closeAllFilesAndShowFirst();
|
||||
}
|
||||
function fileSaved(file, err) {
|
||||
if (err) {
|
||||
errorFiles.push(file.get('name'));
|
||||
}
|
||||
if (--pendingCallbacks === 0) {
|
||||
if (errorFiles.length) {
|
||||
Alerts.error({
|
||||
header: 'Save Error',
|
||||
body: 'Failed to auto-save file' + (errorFiles.length > 1 ? 's: ' : '') + ' ' + errorFiles.join(', ')
|
||||
});
|
||||
} else {
|
||||
that.closeAllFilesAndShowFirst();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
closeAllFilesAndShowFirst: function() {
|
||||
var firstFile = this.model.files.find(function(file) { return !file.get('demo'); });
|
||||
this.model.closeAllFiles();
|
||||
if (firstFile) {
|
||||
this.views.open.showClosedFile(firstFile);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -65,7 +65,8 @@ var ModalView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
closeWithResult: function(result) {
|
||||
this.trigger('result', result);
|
||||
var checked = this.model.checkbox ? this.$el.find('#modal__check').is(':checked') : undefined;
|
||||
this.trigger('result', result, checked);
|
||||
this.$el.addClass('modal--hidden');
|
||||
this.undelegateEvents();
|
||||
setTimeout(this.remove.bind(this), 100);
|
||||
|
|
|
@ -37,10 +37,14 @@ var OpenView = Backbone.View.extend({
|
|||
dropboxLoading: null,
|
||||
|
||||
initialize: function () {
|
||||
this.file = new FileModel();
|
||||
this.setFileModel(new FileModel());
|
||||
this.fileData = null;
|
||||
this.keyFileData = null;
|
||||
this.passwordInput = new SecureInput();
|
||||
},
|
||||
|
||||
setFileModel: function(file) {
|
||||
this.file = file;
|
||||
this.listenTo(this.file, 'change:open', this.fileOpenChanged);
|
||||
this.listenTo(this.file, 'change:opening', this.fileOpeningChanged);
|
||||
this.listenTo(this.file, 'change:error', this.fileErrorChanged);
|
||||
|
@ -202,6 +206,12 @@ var OpenView = Backbone.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
showClosedFile: function(file) {
|
||||
this.setFileModel(file);
|
||||
this.fileData = file.data;
|
||||
this.displayOpenFile();
|
||||
},
|
||||
|
||||
openFile: function() {
|
||||
if (!this.file.get('opening')) {
|
||||
this.openAny('fileData');
|
||||
|
|
|
@ -17,6 +17,7 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
'change .settings__general-expand': 'changeExpandGroups',
|
||||
'change .settings__general-auto-update': 'changeAutoUpdate',
|
||||
'change .settings__general-clipboard': 'changeClipboard',
|
||||
'change .settings__general-auto-save': 'changeAutoSave',
|
||||
'click .settings__general-update-btn': 'checkUpdate',
|
||||
'click .settings__general-restart-btn': 'restartApp',
|
||||
'click .settings__general-download-update-btn': 'downloadUpdate',
|
||||
|
@ -42,6 +43,7 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
expandGroups: AppSettingsModel.instance.get('expandGroups'),
|
||||
canClearClipboard: !!Launcher,
|
||||
clipboardSeconds: AppSettingsModel.instance.get('clipboardSeconds'),
|
||||
autoSave: AppSettingsModel.instance.get('autoSave'),
|
||||
devTools: Launcher && Launcher.devTools,
|
||||
canAutoUpdate: !!Launcher,
|
||||
autoUpdate: Updater.getAutoUpdateType(),
|
||||
|
@ -112,6 +114,11 @@ var SettingsGeneralView = Backbone.View.extend({
|
|||
Updater.check(true);
|
||||
},
|
||||
|
||||
changeAutoSave: function(e) {
|
||||
var autoSave = e.target.checked || false;
|
||||
AppSettingsModel.instance.set('autoSave', autoSave);
|
||||
},
|
||||
|
||||
restartApp: function() {
|
||||
if (Launcher) {
|
||||
Launcher.requestRestart();
|
||||
|
|
|
@ -53,4 +53,7 @@
|
|||
width: 90%;
|
||||
}
|
||||
}
|
||||
&__check-wrap {
|
||||
margin-top: $base-spacing;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
<div class="modal__content">
|
||||
<i class="modal__icon fa fa-<%= icon %>"></i>
|
||||
<div class="modal__header"><%= header %></div>
|
||||
<div class="modal__body"><%= body %></div>
|
||||
<div class="modal__body">
|
||||
<%= body %>
|
||||
<% if (typeof checkbox !== 'undefined') { %>
|
||||
<div class="modal__check-wrap"><input type="checkbox" id="modal__check" /><label for="modal__check"><%- checkbox %></label></div>
|
||||
<% } %>
|
||||
</div>
|
||||
<div class="modal__buttons">
|
||||
<% buttons.forEach(function(btn) { %>
|
||||
<button class="<%= btn.result ? '' : 'btn-error' %>" data-result="<%= btn.result %>"><%= btn.title %></button>
|
||||
<button class="<%= btn.result && !btn.error ? '' : 'btn-error' %>" data-result="<%= btn.result %>"><%= btn.title %></button>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -51,8 +51,8 @@
|
|||
<label for="settings__general-expand">Show entries from all subgroups</label>
|
||||
</div>
|
||||
|
||||
<% if (canClearClipboard) { %>
|
||||
<h2>Function</h2>
|
||||
<% if (canClearClipboard) { %>
|
||||
<div>
|
||||
<label for="settings__general-clipboard">Clear clipboard after copy:</label>
|
||||
<select class="settings__general-clipboard settings__select input-base" id="settings__general-clipboard">
|
||||
|
@ -64,6 +64,11 @@
|
|||
</select>
|
||||
</div>
|
||||
<% } %>
|
||||
<div>
|
||||
<input type="checkbox" class="settings__input input-base settings__general-auto-save" id="settings__general-auto-save"
|
||||
<%- autoSave ? 'checked' : '' %> />
|
||||
<label for="settings__general-auto-save">Auto-save on lock</label>
|
||||
</div>
|
||||
|
||||
<% if (devTools) { %>
|
||||
<h2>Advanced</h2>
|
||||
|
|
Loading…
Reference in New Issue