Lazy Home: Lazy load latest blog posts and assets and group by week and

project.

Javascript tutti.js and timeline.js is needed, and then the following to
init the timeline:

$('.timeline')
    .timeline({
        url: '/api/timeline'
    });

# Javascript Notes:
## ES6 transpile:
* Files in src/scripts/js/es6/common will be transpiled from
modern es6 js to old es5 js, and then added to tutti.js
* Files in src/scripts/js/es6/individual will be transpiled from
modern es6 js to old es5 js to individual module files
## JS Testing
* Added the Jest test framework to write javascript tests.
* `npm test` will run all the javascript tests

Thanks to Sybren for reviewing
This commit is contained in:
2018-11-12 12:57:24 +01:00
parent 1a49b24f8e
commit 37fe235d47
6 changed files with 33 additions and 238 deletions

View File

@@ -2,162 +2,27 @@
* Support for fetching & rendering assets by tags.
*/
(function($) {
/* How many nodes to load initially, and when clicked on the 'Load Next' link. */
const LOAD_INITIAL_COUNT = 8;
const LOAD_NEXT_COUNT = 8;
var mark_if_public = true;
/* Renders a node as an asset card, returns a jQuery object. */
function renderAsset(node) {
let card = $('<a class="card asset card-image-fade pr-0 mx-0 mb-2">')
.addClass('js-tagged-asset')
.attr('href', '/nodes/' + node._id + '/redir')
.attr('title', node.name);
let thumbnail_container = $('<div class="embed-responsive embed-responsive-16by9">');
function warnNoPicture() {
let card_icon = $('<div class="card-img-top card-icon embed-responsive-item">');
card_icon.html('<i class="pi-' + node.node_type + '">');
thumbnail_container.append(card_icon);
}
if (!node.picture) {
warnNoPicture();
} else {
$(window).trigger('pillar:workStart');
// TODO: show 'loading' thingy
$.get('/api/files/' + node.picture)
.fail(function(error) {
let msg = xhrErrorResponseMessage(error);
console.log(msg);
})
.done(function(resp) {
// Render the picture if it has the proper size.
var show_variation = null;
if (typeof resp.variations != 'undefined') {
for (variation of resp.variations) {
if (variation.size != 'm') continue;
show_variation = variation;
break;
}
}
if (show_variation == null) {
warnNoPicture();
return;
}
let img = $('<img class="card-img-top embed-responsive-item">')
.attr('alt', node.name)
.attr('src', variation.link)
.attr('width', variation.width)
.attr('height', variation.height);
thumbnail_container.append(img);
})
.always(function(){
$(window).trigger('pillar:workStop');
});
}
card.append(thumbnail_container);
/* Card body for title and meta info. */
let card_body = $('<div class="card-body py-2 d-flex flex-column">');
let card_title = $('<div class="card-title mb-1 font-weight-bold">');
card_title.text(node.name);
card_body.append(card_title);
let card_meta = $('<ul class="card-text list-unstyled d-flex text-black-50 mt-auto">');
let card_project = $('<a class="font-weight-bold pr-2">')
.attr('href', '/p/' + node.project.url)
.attr('title', node.project.name)
.text(node.project.name);
card_meta.append(card_project);
card_meta.append('<li>' + node.pretty_created + '</li>');
card_body.append(card_meta);
/* Video progress and 'watched' label. */
if (node.view_progress){
let card_progress = $('<div class="progress rounded-0">');
let card_progress_bar = $('<div class="progress-bar">');
card_progress_bar.css('width', node.view_progress.progress_in_percent + '%');
card_progress.append(card_progress_bar);
thumbnail_container.append(card_progress);
if (node.view_progress.done){
let card_progress_done = $('<div class="card-label">WATCHED</div>');
thumbnail_container.append(card_progress_done);
}
}
if (node.properties.duration){
let card_duration = $('<div class="card-label right">' + node.properties.duration + '</div>');
thumbnail_container.append(card_duration);
}
/* 'Free' ribbon for public assets. */
if (mark_if_public && node.permissions && node.permissions.world){
card.addClass('free');
}
card.append(card_body);
return card;
}
function loadNext(card_deck_element) {
let $card_deck = $(card_deck_element);
let tagged_assets = card_deck_element.tagged_assets; // Stored here by loadTaggedAssets().
let already_loaded = $card_deck.find('a.js-tagged-asset').length;
let load_next = $card_deck.find('a.js-load-next');
let nodes_to_load = tagged_assets.slice(already_loaded, already_loaded + LOAD_NEXT_COUNT);
for (node of nodes_to_load) {
let link = renderAsset(node);
load_next.before(link);
}
if (already_loaded + LOAD_NEXT_COUNT >= tagged_assets.length)
load_next.remove();
}
$.fn.loadTaggedAssets = function(LOAD_INITIAL_COUNT, LOAD_NEXT_COUNT, has_subscription) {
$.fn.loadTaggedAssets = function(load_initial_count, load_next_count, has_subscription) {
mark_if_public = !has_subscription;
this.each(function(index, card_deck_element) {
// TODO(Sybren): show a 'loading' animation.
$.get('/api/nodes/tagged/' + card_deck_element.dataset.assetTag)
this.each(function(index, each) {
let $card_deck_element = $(each)
$card_deck_element.trigger('pillar:workStart');
$.get('/api/nodes/tagged/' + $card_deck_element.data('assetTag'))
.fail(function(error) {
let msg = xhrErrorResponseMessage(error);
$('<a>').addClass('bg-danger').text(msg).appendTo(card_deck_element);
$card_deck_element
.append(
$('<p>').addClass('bg-danger').text(msg)
);
})
.done(function(resp) {
// 'resp' is a list of node documents.
// Store the response on the DOM card_deck_element so that we can later render more.
card_deck_element.tagged_assets = resp;
// Here render the first N.
for (node of resp.slice(0, LOAD_INITIAL_COUNT)) {
let li = renderAsset(node);
li.appendTo(card_deck_element);
}
// Don't bother with a 'load next' link if there is no more.
if (resp.length <= LOAD_INITIAL_COUNT) return;
if (LOAD_NEXT_COUNT > 0) {
// Construct the 'load next' link.
let link = $('<a class="btn btn-outline-primary px-5 mb-auto mx-3 btn-block">')
.addClass('js-load-next')
.attr('href', 'javascript:void(0);')
.click(function() { loadNext(card_deck_element); return false; })
.text('Load More');
link.appendTo(card_deck_element);
}
$card_deck_element.append(
pillar.templates.Nodes.createListOf$nodeItems(resp, load_initial_count, load_next_count)
);
})
.always(function() {
$card_deck_element.trigger('pillar:workStop');
});
});
};

View File

@@ -1,11 +0,0 @@
body.homepage
.blog
.jumbotron
padding-top: 6em
padding-bottom: 6em
*
font-size: $h1-font-size
.lead
font-size: $font-size-base

View File

@@ -71,6 +71,7 @@
@import "../../../pillar/src/styles/components/checkbox"
@import "../../../pillar/src/styles/components/overlay"
@import "../../../pillar/src/styles/components/card"
@import "../../../pillar/src/styles/components/timeline"
@import "../../../pillar/src/styles/comments"
@import "../../../pillar/src/styles/notifications"
@@ -82,7 +83,6 @@
@import "../../../pillar/src/styles/_user"
@import _welcome
@import _homepage
@import _services
@import _about
@import "../../../pillar/src/styles/_search"

View File

@@ -29,26 +29,19 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
.container-fluid.dashboard-container.imgs-fluid
.row
.col-md-8.col-xl-9
section.blog
| {% if latest_posts %}
| {% for node in latest_posts %}
| {{ render_blog_post(node) }}
| {% endfor %}
| {% else %}
| No blog entries... yet!
| {% endif %}
section.timeline
.d-block.text-center
a.d-inline-block.p-3.text-muted(href="{{ url_for('main.main_blog') }}")
| See All Blog Posts
.d-block.text-center
a.d-inline-block.p-3.text-muted(href="{{ url_for('main.main_blog') }}")
| See All Blog Posts
a.d-inline-block.p-3.text-muted(
href="{{ url_for('main.feeds_blogs') }}",
title="Blogs Feed",
data-toggle="tooltip",
data-placement="left")
i.pi-rss
| RSS Feed
a.d-inline-block.p-3.text-muted(
href="{{ url_for('main.feeds_blogs') }}",
title="Blogs Feed",
data-toggle="tooltip",
data-placement="left")
i.pi-rss
| RSS Feed
.col-md-4.col-xl-3
section.pt-3
@@ -64,23 +57,6 @@ meta(name="twitter:image", content="{% if main_project.picture_header %}{{ main_
p.text-muted.pt-2.
A poetic short film about a mountain spirit and her wise little dog. #[a.text-muted(href="/p/spring/") Check it out].
section.py-3
h6.title-underline What's Going On
| {% if activity_stream %}
+card-deck()(class='card-deck-vertical pl-3')
| {% for child in activity_stream %}
| {% if child.node_type not in ['comment'] %}
| {{ asset_list_item(child, current_user) }}
| {% endif %}
| {% endfor %}
| {% else %}
.card
.card-body
h6.card-title
| No assets.
| {% endif %}
section.py-3.border-bottom.mb-3
h6.title-underline
a.text-muted(href="{{ url_for('main.nodes_search_index') }}")
@@ -143,6 +119,11 @@ script.
$(this).text($(this).text().replace(/\*|\@|\<(.*?)\>/g, ''));
});
$('.timeline')
.timeline({
url: '/api/timeline'
});
hopToTop(); // Display jump to top button
});
| {% endblock %}

View File

@@ -32,6 +32,7 @@ html(lang="en")
| {% endblock og %}
script(src="{{ url_for('static_pillar', filename='assets/js/tutti.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/timeline.min.js') }}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/jquery.typeahead-0.11.1.min.js')}}")
script(src="{{ url_for('static_pillar', filename='assets/js/vendor/js.cookie-2.0.3.min.js')}}")
| {% if current_user.is_authenticated %}