diff options
-rwxr-xr-x | css/mail.css | 8 | ||||
-rw-r--r-- | js/controller/foldercontroller.js | 54 | ||||
-rw-r--r-- | js/routecontroller.js | 24 | ||||
-rw-r--r-- | js/router.js | 1 | ||||
-rw-r--r-- | js/search.js | 12 | ||||
-rw-r--r-- | js/service/messageservice.js | 8 | ||||
-rw-r--r-- | js/templates/empty-folder.html | 2 | ||||
-rw-r--r-- | js/templates/loading.html | 6 | ||||
-rw-r--r-- | js/templates/message-list.html | 5 | ||||
-rw-r--r-- | js/views/appview.js | 26 | ||||
-rw-r--r-- | js/views/emptyfolderview.js | 33 | ||||
-rw-r--r-- | js/views/foldercontent.js | 5 | ||||
-rw-r--r-- | js/views/loadingview.js | 15 | ||||
-rw-r--r-- | js/views/messagesview.js | 28 | ||||
-rw-r--r-- | js/views/navigation-accounts.js | 2 | ||||
-rw-r--r-- | js/views/nosearchresultmessagelistview.js | 9 |
16 files changed, 176 insertions, 62 deletions
diff --git a/css/mail.css b/css/mail.css index e4e656cae..ed93ad815 100755 --- a/css/mail.css +++ b/css/mail.css @@ -4,6 +4,14 @@ top: 50%; } +.emptycontent { + padding-top: 30vh; + margin-top: 0px; +} +.emptycontent > h2 { + padding-top: 20px; +} + .container { position: relative; width: 100%; diff --git a/js/controller/foldercontroller.js b/js/controller/foldercontroller.js index 7eb8c6b93..e9626f698 100644 --- a/js/controller/foldercontroller.js +++ b/js/controller/foldercontroller.js @@ -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 }; }); diff --git a/js/routecontroller.js b/js/routecontroller.js index 62690f288..480dae3d0 100644 --- a/js/routecontroller.js +++ b/js/routecontroller.js @@ -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); }, diff --git a/js/router.js b/js/router.js index eb3df37c7..139e8d94a 100644 --- a/js/router.js +++ b/js/router.js @@ -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' diff --git a/js/search.js b/js/search.js index 58ade8a5b..12c93ff9b 100644 --- a/js/search.js +++ b/js/search.js @@ -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); + } } } diff --git a/js/service/messageservice.js b/js/service/messageservice.js index d8cdb97be..068a862f3 100644 --- a/js/service/messageservice.js +++ b/js/service/messageservice.js @@ -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(); diff --git a/js/templates/empty-folder.html b/js/templates/empty-folder.html new file mode 100644 index 000000000..88528a5fb --- /dev/null +++ b/js/templates/empty-folder.html @@ -0,0 +1,2 @@ +<div class="icon-mail"></div> +<h2>{{ t 'No messages in this folder!' }}</h2>
\ No newline at end of file diff --git a/js/templates/loading.html b/js/templates/loading.html new file mode 100644 index 000000000..7d4460261 --- /dev/null +++ b/js/templates/loading.html @@ -0,0 +1,6 @@ +{{#if hint}} +<div class="emptycontent"> + <a class="icon-loading"></a> + <h2>{{{ hint }}}</h2> +</div> +{{/if}}
\ No newline at end of file diff --git a/js/templates/message-list.html b/js/templates/message-list.html index 36e5f459c..a117d63f2 100644 --- a/js/templates/message-list.html +++ b/js/templates/message-list.html @@ -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>
\ No newline at end of file diff --git a/js/views/appview.js b/js/views/appview.js index 251185a4e..1494a7f66 100644 --- a/js/views/appview.js +++ b/js/views/appview.js @@ -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); } }); diff --git a/js/views/emptyfolderview.js b/js/views/emptyfolderview.js new file mode 100644 index 000000000..f45e9486d --- /dev/null +++ b/js/views/emptyfolderview.js @@ -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; +}); diff --git a/js/views/foldercontent.js b/js/views/foldercontent.js index c344e4c7f..c5616d0e1 100644 --- a/js/views/foldercontent.js +++ b/js/views/foldercontent.js @@ -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) { diff --git a/js/views/loadingview.js b/js/views/loadingview.js index 4d30e6fdb..db81e9c5f 100644 --- a/js/views/loadingview.js +++ b/js/views/loadingview.js @@ -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; diff --git a/js/views/messagesview.js b/js/views/messagesview.js index 5f73bd5d0..e8501c590 100644 --- a/js/views/messagesview.js +++ b/js/views/messagesview.js @@ -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 diff --git a/js/views/navigation-accounts.js b/js/views/navigation-accounts.js index d1ce964cf..0f1fbf3cd 100644 --- a/js/views/navigation-accounts.js +++ b/js/views/navigation-accounts.js @@ -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')); diff --git a/js/views/nosearchresultmessagelistview.js b/js/views/nosearchresultmessagelistview.js index 1981bd63c..573becf25 100644 --- a/js/views/nosearchresultmessagelistview.js +++ b/js/views/nosearchresultmessagelistview.js @@ -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) }); }); |