From e102ac0292417113732ddc1e007ecfc207879052 Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Wed, 15 Oct 2014 11:06:35 +0200 Subject: [PATCH] Infinite scroll enhancements. see #585 --- modoboa/core/views/admin.py | 2 +- modoboa/extensions/admin/views/domain.py | 2 +- modoboa/extensions/admin/views/identity.py | 2 +- .../amavis/static/amavis/js/quarantine.js | 1 - modoboa/extensions/amavis/views.py | 54 ++++++++++--------- modoboa/extensions/webmail/lib/imaputils.py | 6 ++- .../webmail/static/webmail/js/webmail.js | 9 ++-- modoboa/extensions/webmail/views.py | 17 +++--- modoboa/lib/paginator.py | 8 +-- modoboa/static/js/jquery.infinitescroll.js | 22 ++++---- modoboa/static/js/listing.js | 5 +- 11 files changed, 70 insertions(+), 58 deletions(-) diff --git a/modoboa/core/views/admin.py b/modoboa/core/views/admin.py index 8b22b4923..911544e8b 100644 --- a/modoboa/core/views/admin.py +++ b/modoboa/core/views/admin.py @@ -143,7 +143,7 @@ def logs_page(request, tplname="core/logs_page.html"): else: context = { "rows": render_to_string(tplname, {"logs": page.object_list}), - "page": page.number + "pages": [page.number] } return render_to_json_response(context) diff --git a/modoboa/extensions/admin/views/domain.py b/modoboa/extensions/admin/views/domain.py index bbcdfbff0..64ddfd807 100644 --- a/modoboa/extensions/admin/views/domain.py +++ b/modoboa/extensions/admin/views/domain.py @@ -71,7 +71,7 @@ def _domains(request): 'domains': page.object_list, } ) - context["page"] = page.number + context["pages"] = [page.number] return render_to_json_response(context) diff --git a/modoboa/extensions/admin/views/identity.py b/modoboa/extensions/admin/views/identity.py index ef4498aee..73a3eb8f3 100644 --- a/modoboa/extensions/admin/views/identity.py +++ b/modoboa/extensions/admin/views/identity.py @@ -60,7 +60,7 @@ def _identities(request): "identities": page.object_list } ) - context["page"] = page.number + context["pages"] = [page.number] return render_to_json_response(context) diff --git a/modoboa/extensions/amavis/static/amavis/js/quarantine.js b/modoboa/extensions/amavis/static/amavis/js/quarantine.js index dbb3506e4..3ca47ad36 100644 --- a/modoboa/extensions/amavis/static/amavis/js/quarantine.js +++ b/modoboa/extensions/amavis/static/amavis/js/quarantine.js @@ -83,7 +83,6 @@ Quarantine.prototype = { $(this.options.scroll_container).scrollTop(10); } else { this.update_listing(data, false); - $(this.options.scroll_container).infinite_scroll("pause"); } }, diff --git a/modoboa/extensions/amavis/views.py b/modoboa/extensions/amavis/views.py index a7f3c937e..13e194f42 100644 --- a/modoboa/extensions/amavis/views.py +++ b/modoboa/extensions/amavis/views.py @@ -27,15 +27,15 @@ from .sql_connector import get_connector from .sql_listing import SQLemail -def empty_quarantine(request): +def empty_quarantine(): """Shortcut to use when no content can be displayed.""" content = "
%s
" % _("Empty quarantine") ctx = getctx("ok", level=2, listing=content) return render_to_json_response(ctx) -def get_listing_page(request, connector): - """.""" +def get_listing_pages(request, connector): + """Return listing pages.""" paginator = Paginator( connector.messages_count(), int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) @@ -44,7 +44,13 @@ def get_listing_page(request, connector): page = paginator.getpage(page_id) if not page: return None - return page + pages = [page] + if not page.has_next and page.has_previous and page.items < 40: + pages = [paginator.getpage(page_id - 1)] + pages + email_list = [] + for page in pages: + email_list += connector.fetch(page.id_start, page.id_stop) + return {"pages": [page.number for page in pages], "rows": email_list} @login_required @@ -55,42 +61,42 @@ def listing_page(request): navparams.store() connector = get_connector(user=request.user, navparams=navparams) - page = get_listing_page(request, connector) - if page is None: + context = get_listing_pages(request, connector) + if context is None: context = {"length": 0} navparams["page"] = previous_page_id else: - context = { - "rows": _render_to_string(request, "amavis/emails_page.html", { - "email_list": connector.fetch(page.id_start, page.id_stop) - }), - "page": page.number - } + context["rows"] = _render_to_string( + request, "amavis/emails_page.html", {"email_list": context["rows"]} + ) return render_to_json_response(context) @login_required def _listing(request): + """Listing initial view. + + Called the first time the listing page is displayed. + """ if not request.user.is_superuser and request.user.group != 'SimpleUsers': if not Domain.objects.get_for_admin(request.user).count(): - return empty_quarantine(request) + return empty_quarantine() navparams = QuarantineNavigationParameters(request) navparams.store() connector = get_connector(user=request.user, navparams=navparams) - page = get_listing_page(request, connector) - if page is None: - return empty_quarantine(request) - - content = _render_to_string(request, "amavis/email_list.html", { - "email_list": connector.fetch(page.id_start, page.id_stop), - }) - ctx = getctx("ok", listing=content, page=page.number) + context = get_listing_pages(request, connector) + if context is None: + return empty_quarantine() + context["listing"] = _render_to_string( + request, "amavis/email_list.html", {"email_list": context["rows"]} + ) + del context["rows"] if request.session.get('location', 'listing') != 'listing': - ctx['menu'] = quar_menu() - request.session['location'] = 'listing' - return render_to_json_response(ctx) + context["menu"] = quar_menu() + request.session["location"] = "listingx" + return render_to_json_response(context) @login_required diff --git a/modoboa/extensions/webmail/lib/imaputils.py b/modoboa/extensions/webmail/lib/imaputils.py index d087306f4..f5d735282 100644 --- a/modoboa/extensions/webmail/lib/imaputils.py +++ b/modoboa/extensions/webmail/lib/imaputils.py @@ -664,7 +664,7 @@ class IMAPconnector(object): attdef = bs.find_attachment(partnum) return attdef, data[int(uid)]["BODY[%s]" % partnum] - def fetch(self, start, stop=None, mbox=None, **kwargs): + def fetch(self, start, stop=None, mbox=None): """Retrieve information about messages from the server Issue a FETCH command to retrieve information about one or @@ -685,7 +685,9 @@ class IMAPconnector(object): data = self._cmd("FETCH", range, query) result = [] for uid in submessages: - msg = email.message_from_string(data[int(uid)]['BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)]']) + msg = email.message_from_string( + data[int(uid)]['BODY[HEADER.FIELDS (DATE FROM TO CC SUBJECT)]'] + ) msg['imapid'] = uid if not r'\Seen' in data[int(uid)]['FLAGS']: msg['style'] = 'unseen' diff --git a/modoboa/extensions/webmail/static/webmail/js/webmail.js b/modoboa/extensions/webmail/static/webmail/js/webmail.js index 6a356f39f..ce737b638 100644 --- a/modoboa/extensions/webmail/static/webmail/js/webmail.js +++ b/modoboa/extensions/webmail/static/webmail/js/webmail.js @@ -172,15 +172,16 @@ Webmail.prototype = { var that = this; if ($container.data("infinite-scroll") !== undefined) { - $container.scrollTop(10); - $container.infinite_scroll( - "reset_loaded_pages", parseInt(data.page)); + if (data.pages && data.pages[0] != 1) { + $container.scrollTop(10); + } + $container.infinite_scroll("reset_loaded_pages", data.pages); $container.infinite_scroll("resume"); return; } $container.infinite_scroll({ - initial_page: parseInt(data.page), + initial_pages: data.pages, url: this.options.listing_url, calculate_bottom: function($element) { return $("#emails").height() - $element.height(); diff --git a/modoboa/extensions/webmail/views.py b/modoboa/extensions/webmail/views.py index c99d84334..96e0710e0 100644 --- a/modoboa/extensions/webmail/views.py +++ b/modoboa/extensions/webmail/views.py @@ -127,7 +127,7 @@ def empty(request): get_imapconnector(request).empty(name) content = "
%s
" % _("Empty mailbox") return render_to_json_response({ - 'listing': content, 'mailbox': name + 'listing': content, 'mailbox': name, 'pages': [1] }) @@ -357,11 +357,9 @@ def listmailbox(request, defmailbox="INBOX", update_session=True): int(parameters.get_user(request.user, "MESSAGES_PER_PAGE")) ) page = paginator.getpage(page_id) - + content = "" if page is not None: - email_list = mbc.fetch( - page.id_start, page.id_stop, mbox, nbelems=page.items - ) + email_list = mbc.fetch(page.id_start, page.id_stop, mbox) content = _render_to_string(request, "webmail/email_list.html", { "email_list": email_list, "page": page_id, @@ -369,12 +367,13 @@ def listmailbox(request, defmailbox="INBOX", update_session=True): }) length = len(content) else: - content = "
{}
".format( - _("Empty mailbox") - ) + if page_id == 1: + content = "
{}
".format( + _("Empty mailbox") + ) length = 0 navparams["page"] = previous_page_id - return {"listing": content, "length": length, "page": page_id} + return {"listing": content, "length": length, "pages": [page_id]} def render_compose(request, form, posturl, email=None, insert_signature=False): diff --git a/modoboa/lib/paginator.py b/modoboa/lib/paginator.py index 6ba5dab20..79ecf94dd 100644 --- a/modoboa/lib/paginator.py +++ b/modoboa/lib/paginator.py @@ -25,8 +25,8 @@ class Page(object): @property def items(self): - """The total number of items. (shortcut)""" - return self.paginator.total + """The number of items in this page.""" + return self.id_stop - self.id_start @property def has_previous(self): @@ -61,10 +61,10 @@ class Page(object): @property def last_page(self): """Retrieve the id of the last page.""" - lid = self.items / self.items_per_page + lid = self.paginator.total / self.items_per_page if not lid: return 1 - if self.items % self.items_per_page: + if self.paginator.total % self.items_per_page: lid += 1 return lid diff --git a/modoboa/static/js/jquery.infinitescroll.js b/modoboa/static/js/jquery.infinitescroll.js index 0d590dddd..23de4b23e 100644 --- a/modoboa/static/js/jquery.infinitescroll.js +++ b/modoboa/static/js/jquery.infinitescroll.js @@ -20,10 +20,14 @@ this.$options = options; this.executing = false; this.end_of_results = false; - this.current_page = (isNaN(this.$options.initial_page)) ? - 1 : this.$options.initial_page; - this.loaded_pages = [this.current_page]; - + if (this.$options.initial_pages !== undefined && + this.$options.initial_pages.length > 0) { + this.current_page = this.$options.initial_pages[0]; + this.loaded_pages = this.$options.initial_pages; + } else { + this.current_page = 1; + this.loaded_pages = [this.current_page]; + } this.resume(); }; @@ -38,7 +42,7 @@ on_scroll: function() { var bottom = this.$options.calculate_bottom(this.$element); - if (bottom === undefined) { + if (bottom === undefined || bottom < 0) { return; } if (this.$element.scrollTop() >= bottom) { @@ -128,11 +132,11 @@ * Reset the loaded pages cache. * * @this InfiniteScroll - * @param {Array} current_page - the new current_page + * @param {Array} new_pages - the new loaded pages */ - reset_loaded_pages: function(current_page) { - this.current_page = current_page; - this.loaded_pages = [this.current_page]; + reset_loaded_pages: function(new_pages) { + this.current_page = new_pages[0]; + this.loaded_pages = new_pages; this.end_of_results = false; }, diff --git a/modoboa/static/js/listing.js b/modoboa/static/js/listing.js index fba3c774e..5a9546fc9 100644 --- a/modoboa/static/js/listing.js +++ b/modoboa/static/js/listing.js @@ -136,7 +136,7 @@ Listing.prototype = { */ end_of_list_reached: function(element) { $("#" + this.options.main_table_id).after( - $("
{0}
".format( + $("
{0}
".format( this.options.eor_message )) ); @@ -156,10 +156,11 @@ Listing.prototype = { $scroll_container.scrollTop(0); if (with_infinite_scroll || with_infinite_scroll === undefined) { if ($scroll_container.data("infinite-scroll") !== undefined) { - $scroll_container.infinite_scroll("reset_loaded_pages", data.page); + $scroll_container.infinite_scroll("reset_loaded_pages", data.pages); $scroll_container.infinite_scroll("resume"); } else { $scroll_container.infinite_scroll({ + initial_pages: data.pages, url: this.options.load_page_url, get_args: $.proxy(this.get_load_page_args, this), calculate_bottom: $.proxy(this.calculate_bottom, this),