Merge pull request #1609 from owncloud/search-rework

Search rework
This commit is contained in:
Jan-Christoph Borchardt 2016-08-05 17:49:20 +02:00 коммит произвёл GitHub
Родитель 55220f7386 d7c148df49
Коммит afb70cf07c
16 изменённых файлов: 176 добавлений и 62 удалений

Просмотреть файл

@ -4,6 +4,14 @@
top: 50%;
}
.emptycontent {
padding-top: 30vh;
margin-top: 0px;
}
.emptycontent > h2 {
padding-top: 20px;
}
.container {
position: relative;
width: 100%;

Просмотреть файл

@ -47,7 +47,7 @@ define(function(require) {
* @param {boolean} noSelect
* @returns {undefined}
*/
function loadFolderMessages(account, folder, noSelect) {
function loadFolderMessages(account, folder, noSelect, searchQuery) {
Radio.ui.trigger('composer:leave');
if (require('state').messagesLoading !== null) {
@ -59,7 +59,6 @@ define(function(require) {
// Set folder active
Radio.folder.trigger('setactive', account, folder);
Radio.ui.trigger('content:loading');
$('#load-more-mail-messages').hide();
@ -71,11 +70,15 @@ define(function(require) {
require('state').currentlyLoading = null;
} else {
var loadingMessages = Radio.message.request('entities', account, folder, {
cache: true
cache: true,
filter: searchQuery,
replace: true
});
$.when(loadingMessages).done(function(messages, cached) {
Radio.ui.trigger('foldercontent:show', account, folder);
Radio.ui.trigger('foldercontent:show', account, folder, {
searchQuery: searchQuery
});
require('state').currentlyLoading = null;
require('state').currentAccount = account;
require('state').currentFolder = folder;
@ -84,9 +87,9 @@ define(function(require) {
// Fade out the message composer
$('#mail_new_message').prop('disabled', false);
if (messages.length > 0) {
Radio.ui.trigger('messagesview:messages:add', messages);
Radio.ui.trigger('messagesview:messages:add', messages);
if (messages.length > 0) {
// Fetch first 10 messages in background
_.each(messages.slice(0, 10), function(
message) {
@ -94,15 +97,10 @@ define(function(require) {
});
Radio.message.trigger('load', account, folder, messages.first());
// Show 'Load More' button if there are
// more messages than the pagination limit
if (messages.length > 20) {
$('#load-more-mail-messages')
.fadeIn()
.css('display', 'block');
}
} else {
$('#emptycontent').show();
$('#load-more-mail-messages')
.fadeIn()
.css('display', 'block');
}
if (cached) {
@ -112,7 +110,7 @@ define(function(require) {
}
});
$.when(loadingMessages).fail(function(error) {
$.when(loadingMessages).fail(function() {
// Set the old folder as being active
var folder = require('state').currentFolder;
Radio.folder.trigger('setactive', account, folder);
@ -121,12 +119,16 @@ define(function(require) {
}
}
var loadFolderMessagesDebounced = _.debounce(loadFolderMessages, 1000);
/**
* @param {Account} account
* @param {Folder} folder
* @returns {Promise}
*/
function showFolder(account, folder) {
Radio.ui.trigger('search:set', '');
Radio.ui.trigger('content:loading');
loadFolderMessages(account, folder, false);
// Save current folder
@ -135,8 +137,26 @@ define(function(require) {
require('state').currentFolder = folder;
}
/**
* @param {Account} account
* @param {Folder} folder
* @param {string} query
* @returns {Promise}
*/
function searchFolder(account, folder, query) {
// If this was triggered by a URL change, we set the search input manually
Radio.ui.trigger('search:set', query);
Radio.ui.trigger('composer:leave');
Radio.ui.trigger('content:loading', t('mail', 'Searching for {query}', {
query: query
}));
loadFolderMessagesDebounced(account, folder, false, query);
}
return {
loadFolder: loadFolders,
showFolder: showFolder
showFolder: showFolder,
searchFolder: searchFolder
};
});

Просмотреть файл

@ -38,6 +38,7 @@ define(function(require) {
this.accounts = accounts;
Radio.navigation.on('folder', _.bind(this.showFolder, this));
Radio.navigation.on('search', _.bind(this.searchFolder, this));
Radio.navigation.on('setup', _.bind(this.showSetup, this));
Radio.navigation.on('accountsettings', _.bind(this.showAccountSettings, this));
},
@ -116,6 +117,29 @@ define(function(require) {
}
FolderController.showFolder(account, folder, noSelect);
},
searchFolder: function(accountId, folderId, query) {
if (!query || query === '') {
this.showFolder(accountId, folderId);
return;
}
this._navigate('accounts/' + accountId + '/folders/' + folderId + '/search/' + query);
var account = this.accounts.get(accountId);
if (_.isUndefined(account)) {
// Unknown account id -> redirect
Radio.ui.trigger('error:show', t('mail', 'Invalid account'));
this.default();
return;
}
var folder = account.getFolderById(folderId);
if (_.isUndefined(folder)) {
folder = account.get('folders').at(0);
Radio.ui.trigger('error:show', t('mail', 'Invalid folder'));
this._navigate('accounts/' + accountId + '/folders/' + folder.get('id'));
}
FolderController.searchFolder(account, folder, query);
},
mailTo: function(params) {
this._handleMailto(params);
},

Просмотреть файл

@ -29,6 +29,7 @@ define(function(require) {
appRoutes: {
'': 'default',
'accounts/:accountId/folders/:folderId': 'showFolder',
'accounts/:accountId/folders/:folderId/search/:query': 'searchFolder',
'mailto(?:params)': 'mailTo',
'setup': 'showSetup',
'accounts/:accountId/settings': 'showAccountSettings'

Просмотреть файл

@ -11,18 +11,18 @@
define(function(require) {
'use strict';
var _ = require('underscore');
var Radio = require('radio');
var lastQuery = '';
var debouncedFilter = _.debounce(function debouncedFilterFn(query) {
Radio.ui.trigger('messagesview:filter', query);
}, 1000);
function filter(query) {
if (query !== lastQuery) {
lastQuery = query;
debouncedFilter(query);
if (require('state').currentAccount && require('state').currentFolder) {
var accountId = require('state').currentAccount.get('accountId');
var folderId = require('state').currentFolder.get('id');
Radio.navigation.trigger('search', accountId, folderId, query);
}
}
}

Просмотреть файл

@ -44,10 +44,16 @@ define(function(require) {
var defaults = {
cache: false,
replace: false, // Replace cached folder list
force: false
force: false,
filter: ''
};
_.defaults(options, defaults);
// Do not cache search queries
if (options.filter !== '') {
options.cache = false;
}
// Abort previous requests
if (messageListXhr !== null) {
messageListXhr.abort();

Просмотреть файл

@ -0,0 +1,2 @@
<div class="icon-mail"></div>
<h2>{{ t 'No messages in this folder!' }}</h2>

Просмотреть файл

@ -0,0 +1,6 @@
{{#if hint}}
<div class="emptycontent">
<a class="icon-loading"></a>
<h2>{{{ hint }}}</h2>
</div>
{{/if}}

Просмотреть файл

@ -1,10 +1,5 @@
<div id="mail-message-list-loading"
class="icon-loading-small"
style="display: none"></div>
<div id="emptycontent"
style="display: none;">
<div class="icon-mail"></div>
<h2>{{ t 'No messages in this folder!' }}</h2>
</div>
<div id="mail-message-list"></div>
<div id="load-more-mail-messages"></div>

Просмотреть файл

@ -57,6 +57,7 @@ define(function(require) {
this.listenTo(Radio.ui, 'content:loading', this.showContentLoading);
this.listenTo(Radio.ui, 'title:update', this.updateTitle);
this.listenTo(Radio.ui, 'accountsettings:show', this.showAccountSettings);
this.listenTo(Radio.ui, 'search:set', this.setSearchQuery);
// Hide notification favicon when switching back from
// another browser tab
@ -159,19 +160,20 @@ define(function(require) {
}));
}
},
showFolderContent: function(account, folder) {
showFolderContent: function(account, folder, options) {
this.activeContent = ContentType.FOLDER_CONTENT;
this.content.show(new FolderContentView({
account: account,
folder: folder
}));
// Merge account, folder into a single options object
options.account = account;
options.folder = folder;
this.content.show(new FolderContentView(options));
},
showContentLoading: function() {
if (this.activeContent !== ContentType.LOADING) {
this.activeContent = ContentType.LOADING;
this.content.show(new LoadingView());
}
showContentLoading: function(text) {
this.activeContent = ContentType.LOADING;
this.content.show(new LoadingView({
text: text
}));
},
updateTitle: function() {
var activeEmail = '';
@ -208,6 +210,10 @@ define(function(require) {
account: account
}));
}
},
setSearchQuery: function(val) {
val = val || '';
$('#searchbox').val(val);
}
});

Просмотреть файл

@ -0,0 +1,33 @@
/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
*
* Mail
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
define(function(require) {
'strict';
var Handlebars = require('handlebars');
var Marionette = require('marionette');
var EmptyMessagesTemplate = require('text!templates/empty-folder.html');
var EmptyMessagesView = Marionette.ItemView.extend({
id: 'emptycontent',
template: Handlebars.compile(EmptyMessagesTemplate)
});
return EmptyMessagesView;
});

Просмотреть файл

@ -43,6 +43,7 @@ define(function(require) {
detailView: null,
account: null,
folder: null,
searchQuery: null,
composer: null,
regions: {
messages: '#mail-messages',
@ -51,6 +52,7 @@ define(function(require) {
initialize: function(options) {
this.account = options.account;
this.folder = options.folder;
this.searchQuery = options.searchQuery;
this.listenTo(Radio.ui, 'message:show', this.onShowMessage);
this.listenTo(Radio.ui, 'composer:show', this.onShowComposer);
@ -69,7 +71,8 @@ define(function(require) {
},
onShow: function() {
this.messages.show(new MessagesView({
collection: this.folder.get('messages')
collection: this.folder.get('messages'),
searchQuery: this.searchQuery
}));
},
onShowMessage: function(message) {

Просмотреть файл

@ -20,14 +20,25 @@
define(function(require) {
'use strict';
var Handlebars = require('handlebars');
var Marionette = require('marionette');
var LoadingTemplate = require('text!templates/loading.html');
/**
* @class LoadingView
*/
var LoadingView = Marionette.ItemView.extend({
template: false,
className: 'icon-loading container'
template: Handlebars.compile(LoadingTemplate),
templateHelpers: function() {
return {
hint: this.hint
};
},
className: 'container',
hint: '',
initialize: function(options) {
this.hint = options.text || '';
}
});
return LoadingView;

Просмотреть файл

@ -26,7 +26,8 @@ define(function(require) {
var Radio = require('radio');
var MessagesItemView = require('views/messagesitem');
var MessageListTemplate = require('text!templates/message-list.html');
var NoSearchResultMessageListView = require('views/nosearchresultmessagelistview');
var EmptyFolderView = require('views/emptyfolderview');
var NoSearchResultView = require('views/nosearchresultmessagelistview');
return Backbone.Marionette.CompositeView.extend({
collection: null,
@ -35,13 +36,15 @@ define(function(require) {
childViewContainer: '#mail-message-list',
template: Handlebars.compile(MessageListTemplate),
currentMessage: null,
searchQuery: null,
loadingMore: false,
reloaded: false,
filterCriteria: null,
events: {
'wheel': 'onScroll',
},
initialize: function() {
initialize: function(options) {
this.searchQuery = options.searchQuery;
var _this = this;
Radio.ui.reply('messagesview:collection', function() {
return _this.collection;
@ -50,7 +53,6 @@ define(function(require) {
this.listenTo(Radio.ui, 'messagesview:messages:add', this.addMessages);
this.listenTo(Radio.ui, 'messagesview:messageflag:set', this.setMessageFlag);
this.listenTo(Radio.ui, 'messagesview:filter', this.filterCurrentMailbox);
this.listenTo(Radio.ui, 'messagesview:filter:clear', this.clearFilter);
this.listenTo(Radio.ui, 'messagesview:message:setactive', this.setActiveMessage);
this.listenTo(Radio.message, 'messagesview:message:next', this.selectNextMessage);
this.listenTo(Radio.message, 'messagesview:message:prev', this.selectPreviousMessage);
@ -60,12 +62,16 @@ define(function(require) {
this.$scrollContainer.scroll(_.bind(this.onScroll, this));
},
getEmptyView: function() {
if (this.filterCriteria) {
return NoSearchResultMessageListView;
if (this.searchQuery && this.searchQuery !== '') {
return NoSearchResultView;
} else {
return EmptyFolderView;
}
},
emptyViewOptions: function() {
return {filterCriteria: this.filterCriteria};
return {
searchQuery: this.searchQuery
};
},
setMessageFlag: function(messageId, flag, val) {
var message = this.collection.get(messageId);
@ -191,10 +197,6 @@ define(function(require) {
};
this.loadMessages(true);
},
clearFilter: function() {
$('#searchbox').val('');
this.filterCriteria = null;
},
loadMessages: function(reload) {
reload = reload || false;
var from = this.collection.size();
@ -219,8 +221,8 @@ define(function(require) {
{
from: from,
to: from + 20,
filter: this.filterCriteria ? this.filterCriteria.text : null,
force: true,
filter: this.searchQuery || '',
// Replace cached message list on reload
replace: reload
});
@ -254,6 +256,8 @@ define(function(require) {
_this.loadingMore = false;
},
});
} else {
_this.loadingMore = false;
}
// Reload scrolls the list to the top, hence a unwanted
// scroll event is fired, which we want to ignore

Просмотреть файл

@ -39,8 +39,6 @@ define(function(require) {
* @returns {undefined}
*/
setFolderActive: function(account, folder) {
Radio.ui.trigger('messagesview:filter:clear');
// disable all other folders for all accounts
require('state').accounts.each(function(acnt) {
var localAccount = require('state').folderView.collection.get(acnt.get('accountId'));

Просмотреть файл

@ -5,7 +5,7 @@
* later. See the COPYING file.
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @copyright Christoph Wurst 2015
* @copyright Christoph Wurst 2015, 2016
*/
define(function(require) {
@ -18,12 +18,9 @@ define(function(require) {
return Marionette.ItemView.extend({
initialize: function(options) {
this.model.set('searchTerm', options.filterCriteria.text || '');
this.model.set('searchTerm', options.searchQuery);
},
template: Handlebars.compile(NoSearchResultMessageListViewTemplate),
onRender: function() {
this.$('#load-more-mail-messages').hide();
}
template: Handlebars.compile(NoSearchResultMessageListViewTemplate)
});
});