mirror of https://github.com/keeweb/keeweb.git
redesigned Dropbox chooser
This commit is contained in:
parent
577d23e212
commit
7d13b49093
|
@ -186,6 +186,7 @@
|
|||
"openErrorDescription": "There was an error opening file",
|
||||
"openErrorFileNotFound": "File not found",
|
||||
"openListErrorBody": "There was an error loading file list",
|
||||
"openShowAllFiles": "Show all files",
|
||||
|
||||
"detAttDownload": "Shift-click attachment button to download or",
|
||||
"detAttDelToRemove": "Delete to remove",
|
||||
|
|
|
@ -273,23 +273,23 @@ const StorageDropbox = StorageBase.extend({
|
|||
});
|
||||
},
|
||||
|
||||
list: function(callback) {
|
||||
list: function(dir, callback) {
|
||||
this.logger.debug('List');
|
||||
const ts = this.logger.ts();
|
||||
this._apiCall({
|
||||
method: 'files/list_folder',
|
||||
data: {
|
||||
path: this._toFullPath(''),
|
||||
path: this._toFullPath(dir || ''),
|
||||
recursive: false
|
||||
},
|
||||
success: data => {
|
||||
this.logger.debug('Listed', this.logger.ts(ts));
|
||||
const fileList = data.entries
|
||||
.filter(f => f['.tag'] === 'file' && f.rev && UrlUtil.isKdbx(f.name))
|
||||
.map(f => ({
|
||||
name: f.name,
|
||||
path: this._toRelPath(f['path_display']),
|
||||
ref: f.rev
|
||||
rev: f.rev,
|
||||
dir: f['.tag'] !== 'file'
|
||||
}));
|
||||
callback(null, fileList);
|
||||
},
|
||||
|
|
|
@ -126,7 +126,7 @@ const StorageGDrive = StorageBase.extend({
|
|||
});
|
||||
},
|
||||
|
||||
list: function(callback) {
|
||||
list: function(dir, callback) {
|
||||
this._oauthAuthorize((err) => {
|
||||
if (err) { return callback && callback(err); }
|
||||
this.logger.debug('List');
|
||||
|
|
|
@ -136,7 +136,7 @@ const StorageOneDrive = StorageBase.extend({
|
|||
});
|
||||
},
|
||||
|
||||
list: function(callback) {
|
||||
list: function(dir, callback) {
|
||||
this._oauthAuthorize(err => {
|
||||
if (err) { return callback && callback(err); }
|
||||
this.logger.debug('List');
|
||||
|
|
|
@ -39,6 +39,10 @@ const ModalView = Backbone.View.extend({
|
|||
el.removeClass('modal--hidden');
|
||||
document.activeElement.blur();
|
||||
}, 20);
|
||||
if (this.model.view) {
|
||||
this.model.view.setElement(this.$el.find('.modal__body'));
|
||||
this.model.view.render();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@ const Keys = require('../const/keys');
|
|||
const Alerts = require('../comp/alerts');
|
||||
const SecureInput = require('../comp/secure-input');
|
||||
const DropboxChooser = require('../comp/dropbox-chooser');
|
||||
const StorageFileListView = require('../views/storage-file-list-view');
|
||||
const FeatureDetector = require('../util/feature-detector');
|
||||
const Logger = require('../util/logger');
|
||||
const Locale = require('../util/locale');
|
||||
const UrlUtil = require('../util/url-util');
|
||||
const InputFx = require('../util/input-fx');
|
||||
const Comparators = require('../util/comparators');
|
||||
const Storage = require('../storage');
|
||||
const Launcher = require('../comp/launcher');
|
||||
|
||||
|
@ -594,7 +596,7 @@ const OpenView = Backbone.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
listStorage: function(storage) {
|
||||
listStorage: function(storage, config) {
|
||||
if (this.busy) {
|
||||
return;
|
||||
}
|
||||
|
@ -602,7 +604,7 @@ const OpenView = Backbone.View.extend({
|
|||
const icon = this.$el.find('.open__icon-storage[data-storage=' + storage.name + ']');
|
||||
this.busy = true;
|
||||
icon.toggleClass('flip3d', true);
|
||||
storage.list((err, files) => {
|
||||
storage.list(config && config.dir, (err, files) => {
|
||||
icon.toggleClass('flip3d', false);
|
||||
this.busy = false;
|
||||
if (err || !files) {
|
||||
|
@ -615,32 +617,53 @@ const OpenView = Backbone.View.extend({
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const buttons = [];
|
||||
const allStorageFiles = {};
|
||||
files.forEach(file => {
|
||||
const fileName = UrlUtil.getDataFileName(file.name);
|
||||
buttons.push({result: file.path, title: fileName});
|
||||
allStorageFiles[file.path] = file;
|
||||
});
|
||||
if (!buttons.length) {
|
||||
if (!files.length) {
|
||||
Alerts.error({
|
||||
header: Locale.openNothingFound,
|
||||
body: Locale.openNothingFoundBody
|
||||
});
|
||||
return;
|
||||
}
|
||||
buttons.push({result: '', title: Locale.alertCancel});
|
||||
|
||||
const fileNameComparator = Comparators.stringComparator('path', true);
|
||||
files.sort((x, y) => {
|
||||
if (x.dir !== y.dir) {
|
||||
return !!y.dir - !!x.dir;
|
||||
}
|
||||
return fileNameComparator(x, y);
|
||||
});
|
||||
if (config && config.dir) {
|
||||
files.unshift({
|
||||
path: config.prevDir,
|
||||
name: '..',
|
||||
dir: true
|
||||
});
|
||||
}
|
||||
const listView = new StorageFileListView({
|
||||
model: {
|
||||
files,
|
||||
showHiddenFiles: config && config.showHiddenFiles
|
||||
}
|
||||
});
|
||||
listView.on('selected', file => {
|
||||
if (file.dir) {
|
||||
this.listStorage(storage, {
|
||||
dir: file.path,
|
||||
prevDir: config && config.dir || '',
|
||||
showHiddenFiles: true
|
||||
});
|
||||
} else {
|
||||
this.openStorageFile(storage, file);
|
||||
}
|
||||
});
|
||||
Alerts.alert({
|
||||
header: Locale.openSelectFile,
|
||||
body: Locale.openSelectFileBody,
|
||||
icon: storage.icon || 'files-o',
|
||||
buttons: buttons,
|
||||
buttons: [{result: '', title: Locale.alertCancel}],
|
||||
esc: '',
|
||||
click: '',
|
||||
success: file => {
|
||||
this.openStorageFile(storage, allStorageFiles[file]);
|
||||
}
|
||||
view: listView
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -239,7 +239,7 @@ const SettingsFileView = Backbone.View.extend({
|
|||
return;
|
||||
}
|
||||
this.model.set('syncing', true);
|
||||
storage.list((err, files) => {
|
||||
storage.list('', (err, files) => {
|
||||
this.model.set('syncing', false);
|
||||
if (err) {
|
||||
return;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
const Backbone = require('backbone');
|
||||
const UrlUtil = require('../util/url-util');
|
||||
|
||||
const StorageFileListView = Backbone.View.extend({
|
||||
template: require('templates/storage-file-list.hbs'),
|
||||
|
||||
events: {
|
||||
'click .open-list__file': 'fileClick',
|
||||
'click .open-list__check-wrap': 'showAllCheckClick'
|
||||
},
|
||||
|
||||
initialize() {
|
||||
this.allStorageFiles = {};
|
||||
this.showHiddenFiles = !!this.model.showHiddenFiles;
|
||||
},
|
||||
|
||||
render() {
|
||||
let files = this.model.files.map(file => {
|
||||
this.allStorageFiles[file.path] = file;
|
||||
return {
|
||||
path: file.path,
|
||||
name: UrlUtil.getDataFileName(file.name),
|
||||
kdbx: UrlUtil.isKdbx(file.name),
|
||||
dir: file.dir
|
||||
};
|
||||
});
|
||||
let hasHiddenFiles = this.showHiddenFiles;
|
||||
if (!this.showHiddenFiles) {
|
||||
const allFilesLength = files.length;
|
||||
files = files.filter(f => !f.dir && f.kdbx);
|
||||
hasHiddenFiles = files.length - allFilesLength;
|
||||
}
|
||||
const density = files.length > 14 ? 3 : files.length > 7 ? 2 : 1;
|
||||
this.renderTemplate({
|
||||
files,
|
||||
density,
|
||||
showHiddenFiles: this.showHiddenFiles,
|
||||
hasHiddenFiles: hasHiddenFiles
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
fileClick(e) {
|
||||
const result = $(e.target).closest('.open-list__file').data('path');
|
||||
const file = this.allStorageFiles[result];
|
||||
this.trigger('selected', file);
|
||||
},
|
||||
|
||||
showAllCheckClick(e) {
|
||||
e.stopPropagation();
|
||||
this.showHiddenFiles = !this.showHiddenFiles;
|
||||
this.render();
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = StorageFileListView;
|
|
@ -231,3 +231,47 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.open-list {
|
||||
&__content {
|
||||
margin: $base-padding-v 0 $base-padding-v (-$base-padding-h);
|
||||
max-height: calc(100vh - 22em);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
&__scrollable {
|
||||
max-height: calc(100vh - 22em);
|
||||
width: calc(100% + 50px);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
&__files {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: calc(100% - 50px);
|
||||
}
|
||||
&__file {
|
||||
cursor: pointer;
|
||||
padding: $base-padding;
|
||||
border-radius: $base-border-radius;
|
||||
box-sizing: border-box;
|
||||
flex-basis: 100%;
|
||||
@include nomobile {
|
||||
.open-list--density2 & { flex-basis: 50%; }
|
||||
.open-list--density3 & { flex-basis: 33.33333%; }
|
||||
}
|
||||
&:hover {
|
||||
@include th { background-color: th(action-background-color-focus-tr); }
|
||||
}
|
||||
&-icon {
|
||||
margin-right: .3em;
|
||||
}
|
||||
&--another {
|
||||
@include th { color: th(medium-color); }
|
||||
}
|
||||
}
|
||||
&__check-label {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<div class="open-list open-list--density{{density}}">
|
||||
<div class="open-list__content">
|
||||
<div class="open-list__scrollable">
|
||||
<div class="open-list__files">
|
||||
{{#each files as |file|}}
|
||||
{{#if file.dir}}
|
||||
<div class="open-list__file" data-path="{{file.path}}">
|
||||
<i class="open-list__file-icon fa fa-{{#ifeq file.name '..'}}arrow-left{{else}}folder-o{{/ifeq}}"></i>
|
||||
<span class="open-list__file-text">{{file.name}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="open-list__files">
|
||||
{{#each files as |file|}}
|
||||
{{#unless file.dir}}
|
||||
<div class="open-list__file {{#unless file.kdbx}}open-list__file--another{{/unless}}" data-path="{{file.path}}">
|
||||
<i class="open-list__file-icon fa fa-{{#if file.kdbx}}key{{else}}file-text-o{{/if}}"></i>
|
||||
<span class="open-list__file-text">{{file.name}}</span>
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{#if hasHiddenFiles}}
|
||||
<div class="open-list__check-wrap">
|
||||
<input type="checkbox" id="open-list__check" {{#if showHiddenFiles}}checked{{/if}}
|
||||
/><label class="open-list__check-label" for="open-list__check">{{res 'openShowAllFiles'}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -3,6 +3,7 @@ Release notes
|
|||
##### v1.6.0 (WIP)
|
||||
`+` desktop apps integrity protection
|
||||
`+` auto-lock on computer lock
|
||||
`+` redesigned Dropbox chooser
|
||||
`+` safari tab icons
|
||||
`*` prevent master password autocomplete
|
||||
`*` build with node.js 8
|
||||
|
|
Loading…
Reference in New Issue