Skip to content

Commit

Permalink
enabling progressive loading of index page content
Browse files Browse the repository at this point in the history
  • Loading branch information
Pomax committed Aug 7, 2019
1 parent 0695518 commit 0d57f46
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 24 deletions.
1 change: 1 addition & 0 deletions network-api/networkapi/settings.py
Expand Up @@ -156,6 +156,7 @@
'wagtail.core',
'wagtail.contrib.forms',
'wagtail.contrib.redirects',
'wagtail.contrib.routable_page',
'wagtail.contrib.styleguide' if DEBUG else None,
'wagtail.contrib.modeladmin',
'experiments',
Expand Down
@@ -0,0 +1,18 @@
# Generated by Django 2.2.3 on 2019-08-07 18:59

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('wagtailpages', '0071_indexpage'),
]

operations = [
migrations.AddField(
model_name='indexpage',
name='page_size',
field=models.IntegerField(choices=[(4, '4'), (8, '8'), (12, '12'), (24, '24')], default=12, help_text='The number of entries to show by default, and per incremental load'),
),
]
70 changes: 58 additions & 12 deletions network-api/networkapi/wagtailpages/models.py
Expand Up @@ -2,10 +2,13 @@

from django.db import models
from django.conf import settings
from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, JsonResponse
from django.template import loader

from taggit.models import Tag

from . import customblocks
from wagtail.contrib.routable_page.models import RoutablePageMixin, route
from wagtail.core import blocks
from wagtail.core.models import Page
from wagtail.core.fields import StreamField, RichTextField
Expand Down Expand Up @@ -532,7 +535,7 @@ def get_context(self, request):
return get_page_tree_information(self, context)


class IndexPage(FoundationMetadataPageMixin, Page):
class IndexPage(FoundationMetadataPageMixin, RoutablePageMixin, Page):
"""
This is a page type for creating "index" pages that
can show cards for all their child content.
Expand All @@ -551,18 +554,67 @@ class IndexPage(FoundationMetadataPageMixin, Page):
help_text='Intro paragraph to show in hero cutout box'
)

DEFAULT_PAGE_SIZE = 12

PAGE_SIZES = (
(4, '4'),
(8, '8'),
(DEFAULT_PAGE_SIZE, str(DEFAULT_PAGE_SIZE)),
(24, '24'),
)

page_size = models.IntegerField(
choices=PAGE_SIZES,
default=DEFAULT_PAGE_SIZE,
help_text='The number of entries to show by default, and per incremental load'
)

content_panels = Page.content_panels + [
FieldPanel('header'),
FieldPanel('intro'),
FieldPanel('page_size'),
]

def get_entries(self):
return self.get_children().live().order_by('-first_published_at')

def get_context(self, request):
context = super().get_context(request)
context = set_main_site_nav_information(self, context, 'Homepage')
context = get_page_tree_information(self, context)
context['entries'] = self.get_children().live().order_by('-first_published_at')
context['entries'] = self.get_entries()[0:self.page_size]
return context

@route('entries')
def generate_entries_set_html(self, request, *args, **kwargs):
"""
Get a set of entries, as rendered HTML
"""

page = 1

if 'page' in request.GET:
page = int(request.GET['page'])

page_size = self.page_size

if 'page_size' in request.GET:
page_size = int(request.GET['page_size'])

start = page * page_size
end = start + page_size
entries = self.get_entries()

return JsonResponse({
'entries_html': loader.render_to_string(
'wagtailpages/fragments/entry_cards.html',
{
'entries': entries[start:end]
}
),
'has_next': end < len(entries)
})


class NewsPage(PrimaryPage):
parent_page_types = ['Homepage']
Expand All @@ -574,7 +626,6 @@ class BlogPageTag(TaggedItemBase):


class BlogPage(FoundationMetadataPageMixin, Page):
template = 'wagtailpages/blog_page.html'

author = models.CharField(
verbose_name='Author',
Expand All @@ -600,24 +651,19 @@ class BlogPage(FoundationMetadataPageMixin, Page):
('quote', customblocks.QuoteBlock()),
])

# Editor panels configuration
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)

zen_nav = True

content_panels = Page.content_panels + [
FieldPanel('author'),
StreamFieldPanel('body'),
]

# Promote panels configuration
tags = ClusterTaggableManager(through=BlogPageTag, blank=True)

promote_panels = FoundationMetadataPageMixin.promote_panels + [
FieldPanel('tags'),
]

# Database fields

zen_nav = True

def get_context(self, request):
context = super().get_context(request)
context['related_posts'] = get_content_related_by_tag(self)
Expand Down
@@ -0,0 +1,9 @@
{% for entry in entries %}
{% with type=entry.specific_class.get_verbose_name|lower %}
{% if type == "blog page" %}
{% include "./blog-card.html" with page=entry %}
{% else %}
{% include "./generic-card.html" with page=entry %}
{% endif %}
{% endwith %}
{% endfor %}
Expand Up @@ -20,20 +20,20 @@ <h1 class="h1-heading mb-0 mt-1 pt-2">
</div>
</div>

<div class="row">
<div class="index-entries row">
{% block subcontent %}

{% for entry in entries %}
{% with type=entry.specific_class.get_verbose_name|lower %}
{% if type == "blog page" %}
{% include "./fragments/blog-card.html" with page=entry %}
{% else %}
{% include "./fragments/generic-card.html" with page=entry %}
{% endif %}
{% endwith %}
{% endfor %}

{% include "./fragments/entry_cards.html" %}
{% endblock %}
</div>

<div class="row">
<div class="col-12 text-center mb-5">
<button class="btn btn-primary load-more-index-entries" data-page-size="{{ page.specific.page_size }}" >
Load more results
</button>

{# See main.js for the javascript that hooks into this button #}
</div>
</div>
</div>
{% endblock %}
43 changes: 43 additions & 0 deletions source/js/main.js
Expand Up @@ -520,6 +520,49 @@ let main = {

injectDonateModal(donationModal, modalOptions);
}

// Enable the "load more results" button on index pages
let loadMoreButton = document.querySelector(`.load-more-index-entries`);
if (loadMoreButton) {
const entries = document.querySelector(`.index-entries`);

// Get the page size from the document, which the IndexPage should
// have templated into its button as a data-page-size attribute.
const pageSize = parseInt(loadMoreButton.dataset.pageSize) || 12;

// Start at page 1, as page 0 is the same sat as the initial page set.
let page = 1;

const loadMoreResults = evt => {
// Construct our API call as a relative URL:
let url = `./entries/?page=${page++}&page_size=${pageSize}`;

// And then fetch the results and render them into the page.
fetch(url)
.then(result => result.json())
.then(data => {
if (!data.has_next) {
loadMoreButton.removeEventListener(`click`, loadMoreResults)
loadMoreButton.parentNode.removeChild(loadMoreButton);
}
return data.entries_html;
})
.then(entries_html => {
const div = document.createElement(`div`);
div.innerHTML = entries_html;

Array.from(div.children).forEach(child =>
entries.appendChild(child)
);
})
.catch(err => {
// TODO: what do we want to do in this case?
console.error(err);
});
}

loadMoreButton.addEventListener(`click`, loadMoreResults);
}
}
};

Expand Down

0 comments on commit 0d57f46

Please sign in to comment.