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:
@@ -42,36 +42,6 @@ def _homepage_context() -> dict:
|
||||
|
||||
# Get latest blog posts
|
||||
api = system_util.pillar_api()
|
||||
latest_posts = Node.all({
|
||||
'projection': {
|
||||
'name': 1,
|
||||
'project': 1,
|
||||
'node_type': 1,
|
||||
'picture': 1,
|
||||
'properties.url': 1,
|
||||
'properties.content': 1,
|
||||
'properties.attachments': 1,
|
||||
'properties.status': 1,
|
||||
},
|
||||
|
||||
'where': {'node_type': 'post', 'properties.status': 'published'},
|
||||
'embedded': {'project': 1},
|
||||
'sort': '-_created',
|
||||
'max_results': '3'
|
||||
}, api=api)
|
||||
|
||||
# Append picture Files to last_posts
|
||||
for post in latest_posts._items:
|
||||
post.picture = get_file(post.picture, api=api)
|
||||
post.url = url_for_node(node=post)
|
||||
|
||||
# Get latest assets added to any project
|
||||
latest_assets = Node.latest('assets', api=api)
|
||||
|
||||
# Append picture Files to latest_assets
|
||||
for asset in latest_assets._items:
|
||||
asset.picture = get_file(asset.picture, api=api)
|
||||
asset.url = url_for_node(node=asset)
|
||||
|
||||
# Get latest comments to any node
|
||||
latest_comments = Node.latest('comments', api=api)
|
||||
@@ -116,20 +86,9 @@ def _homepage_context() -> dict:
|
||||
main_project = Project.find(current_app.config['MAIN_PROJECT_ID'], api=api)
|
||||
main_project.picture_header = get_file(main_project.picture_header, api=api)
|
||||
|
||||
# Merge latest assets and comments into one activity stream.
|
||||
def sort_key(item):
|
||||
return item._created
|
||||
|
||||
activity_stream = sorted(latest_assets._items, key=sort_key, reverse=True)
|
||||
|
||||
for node in activity_stream:
|
||||
node.url = url_for_node(node=node)
|
||||
|
||||
return dict(
|
||||
main_project=main_project,
|
||||
latest_posts=latest_posts._items,
|
||||
latest_comments=latest_comments._items,
|
||||
activity_stream=activity_stream,
|
||||
random_featured=random_featured)
|
||||
|
||||
|
||||
|
@@ -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');
|
||||
});
|
||||
});
|
||||
};
|
||||
|
@@ -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
|
@@ -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"
|
||||
|
@@ -29,14 +29,7 @@ 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') }}")
|
||||
@@ -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 %}
|
||||
|
@@ -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 %}
|
||||
|
Reference in New Issue
Block a user