docbrown/app/assets/javascripts/initializers/initScrolling.js

451 lines
12 KiB
JavaScript

/* global insertAfter, insertArticles, buildArticleHTML, nextPage:writable, fetching:writable, done:writable, InstantClick */
var client;
function fetchNext(el, endpoint, insertCallback) {
var indexParams = JSON.parse(el.dataset.params);
var urlParams = Object.keys(indexParams)
.map(function handleMap(k) {
return encodeURIComponent(k) + '=' + encodeURIComponent(indexParams[k]);
})
.join('&');
if (urlParams.indexOf('q=') > -1) {
return;
}
var fetchUrl = (
endpoint +
'?page=' +
nextPage +
'&' +
urlParams +
'&signature=' +
parseInt(Date.now() / 400000, 10)
).replace('&&', '&');
window
.fetch(fetchUrl)
.then(function handleResponse(response) {
response.json().then(function insertEntries(entries) {
nextPage += 1;
insertCallback(entries);
if (entries.length === 0) {
document.getElementById('loading-articles').style.display = 'none';
done = true;
}
});
})
.catch(function logError(err) {
// eslint-disable-next-line no-console
console.log(err);
});
}
function insertNext(params, buildCallback) {
return function insertEntries(entries) {
var list = document.getElementById(params.listId || 'sublist');
var newFollowersHTML = '';
entries.forEach(function insertAnEntry(entry) {
let existingEl = document.getElementById(
(params.elId || 'element') + '-' + entry.id,
);
if (!existingEl) {
var newHTML = buildCallback(entry);
newFollowersHTML += newHTML;
}
});
var distanceFromBottom =
document.documentElement.scrollHeight - document.body.scrollTop;
var newNode = document.createElement('div');
newNode.innerHTML = newFollowersHTML;
var singleArticles = document.querySelectorAll(
'.single-article, .crayons-story',
);
var lastElement = singleArticles[singleArticles.length - 1];
insertAfter(newNode, lastElement);
if (nextPage > 0) {
fetching = false;
}
};
}
function buildFollowsHTML(follows) {
return (
'<div id="follows-' +
follows.id +
'" class="single-article">\n' +
' <a href="' +
follows.path +
'" class="block-link">\n' +
' <h2>\n' +
' <img alt="' +
follows.username +
' profile image" src="' +
follows.profile_image +
'" />\n' +
' ' +
follows.name +
'\n' +
' <span class="dashboard-username">@' +
follows.username +
'</span>\n' +
' </h2>\n' +
' </a>\n' +
'</div>'
);
}
var negativeFollow = -1;
function buildTagsHTML(tag) {
var tagHTML = '';
if (tag.points < 0 && negativeFollow === -1) {
tagHTML += '<h2>Negative Tags (anti-follows)</h2>';
negativeFollow = 0;
}
return (
tagHTML +
'<div id="follows-' +
tag.id +
'" class="single-article" style="border: 1px solid ' +
tag.color +
';box-shadow: 3px 3px 0px ' +
tag.color +
'">\n' +
' <h2>\n' +
' <a href="/t/' +
tag.name +
'">\n' +
' ' +
tag.name +
'\n' +
' </a>\n' +
' <form class="edit_follow" id="edit_follow_' +
tag.id +
'" action="/follows/' +
tag.id +
'" accept-charset="UTF-8" method="post">\n' +
' <input name="utf8" type="hidden" value="✓">\n' +
' <input type="hidden" name="_method" value="patch">\n' +
' <input type="hidden" name="authenticity_token" value="' +
tag.token +
'">\n' +
' <label for="follow_points">Follow Weight:</label>\n' +
' <input step="any" required="required" type="number" value="' +
tag.points +
'" name="follow[points]" id="follow_points">\n' +
' <input type="submit" name="commit" value="Submit">\n' +
' </form>\n' +
' </h2>\n' +
'</div>'
);
}
function fetchNextFollowingPage(el) {
var indexParams = JSON.parse(el.dataset.params);
var action = indexParams.action;
if (action.includes('users')) {
fetchNext(
el,
'/followings/users',
insertNext({ elId: 'follows' }, buildFollowsHTML),
);
} else if (action.includes('podcasts')) {
fetchNext(
el,
'/followings/podcasts',
insertNext({ elId: 'follows' }, buildFollowsHTML),
);
} else if (action.includes('organizations')) {
fetchNext(
el,
'/followings/organizations',
insertNext({ elId: 'follows' }, buildFollowsHTML),
);
} else {
fetchNext(
el,
'/followings/tags',
insertNext({ elId: 'follows' }, buildTagsHTML),
);
}
}
function fetchNextFollowersPage(el) {
fetchNext(
el,
'/api/followers/users',
insertNext({ elId: 'follows' }, buildFollowsHTML),
);
}
function buildVideoArticleHTML(videoArticle) {
return (
'<a class="single-video-article single-article" href="' +
videoArticle.path +
'" id="video-article-' +
videoArticle.id +
'">\n' +
' <div class="video-image" style="background-image: url(' +
videoArticle.cloudinary_video_url +
')">\n' +
' <span class="video-timestamp">' +
videoArticle.video_duration_in_minutes +
'</span>\n' +
' </div>\n' +
' <p><strong>' +
videoArticle.title +
'</strong></p>\n' +
' <p>' +
videoArticle.user.name +
'</p>\n' +
'</a>'
);
}
function insertVideos(videoArticles) {
var list = document.getElementById('subvideos');
var newVideosHTML = '';
videoArticles.forEach(function insertAVideo(videoArticle) {
var existingEl = document.getElementById(
'video-article-' + videoArticle.id,
);
if (!existingEl) {
var newHTML = buildVideoArticleHTML(videoArticle);
newVideosHTML += newHTML;
}
});
var distanceFromBottom =
document.documentElement.scrollHeight - document.body.scrollTop;
var newNode = document.createElement('div');
newNode.innerHTML = newVideosHTML;
newNode.className += 'video-collection';
var singleArticles = document.querySelectorAll(
'.single-article, .crayons-story',
);
var lastElement = singleArticles[singleArticles.length - 1];
insertAfter(newNode, lastElement);
if (nextPage > 0) {
fetching = false;
}
}
function fetchNextVideoPage(el) {
fetchNext(el, '/api/videos', insertVideos);
}
function insertArticles(articles) {
var list = document.getElementById('substories');
var newArticlesHTML = '';
var el = document.getElementById('home-articles-object');
if (el) {
el.outerHTML = '';
}
articles.forEach(function insertAnArticle(article) {
var existingEl = document.getElementById('article-link-' + article.id);
if (
![
'/',
'/top/week',
'/top/month',
'/top/year',
'/top/infinity',
'/latest',
].includes(window.location.pathname) &&
existingEl &&
existingEl.parentElement &&
existingEl.parentElement.classList.contains('crayons-story') &&
!document.getElementById('video-player-' + article.id)
) {
existingEl.parentElement.outerHTML = buildArticleHTML(article);
} else if (!existingEl) {
var newHTML = buildArticleHTML(article);
newArticlesHTML += newHTML;
initializeReadingListIcons();
}
});
var distanceFromBottom =
document.documentElement.scrollHeight - document.body.scrollTop;
var newNode = document.createElement('div');
newNode.classList.add('paged-stories');
newNode.innerHTML = newArticlesHTML;
newNode.addEventListener('click', (event) => {
const { classList } = event.target;
// This looks a little messy, but it's the only
// way to make the entire card clickable.
if (
classList.contains('crayons-story') ||
classList.contains('crayons-story__top') ||
classList.contains('crayons-story__body') ||
classList.contains('crayons-story__indention') ||
classList.contains('crayons-story__title') ||
classList.contains('crayons-story__tags') ||
classList.contains('crayons-story__bottom')
) {
let element = event.target;
let { articlePath } = element.dataset;
while (!articlePath) {
articlePath = element.dataset.articlePath;
element = element.parentElement;
}
InstantClick.preload(articlePath);
InstantClick.display(articlePath);
}
});
var singleArticles = document.querySelectorAll(
'.single-article, .crayons-story',
);
var lastElement = singleArticles[singleArticles.length - 1];
insertAfter(newNode, lastElement);
if (nextPage > 0) {
fetching = false;
}
}
function fetchNextPodcastPage(el) {
fetchNext(el, '/api/podcast_episodes', insertArticles);
}
function paginate(tag, params, requiresApproval) {
const searchHash = {
...{ per_page: 15, page: nextPage },
...JSON.parse(params),
};
if (tag && tag.length > 0) {
searchHash.tag_names = searchHash.tag_names || [];
searchHash.tag_names.push(tag);
}
searchHash.approved = requiresApproval === 'true' ? 'true' : '';
var homeEl = document.getElementById('index-container');
if (homeEl.dataset.feed === 'base-feed') {
searchHash.class_name = 'Article';
} else if (homeEl.dataset.feed === 'latest') {
searchHash.class_name = 'Article';
searchHash.sort_by = 'published_at';
} else {
searchHash.class_name = 'Article';
searchHash['published_at[gte]'] = homeEl.dataset.articlesSince;
searchHash.sort_by = 'public_reactions_count';
}
// Brute force copying code from a utlity for quick fix
const searchParams = new URLSearchParams();
Object.keys(searchHash).forEach((key) => {
const value = searchHash[key];
if (Array.isArray(value)) {
value.forEach((arrayValue) => {
searchParams.append(`${key}[]`, arrayValue);
});
} else {
searchParams.append(key, value);
}
});
fetch(`/search/feed_content?${searchParams.toString()}`, {
method: 'GET',
headers: {
Accept: 'application/json',
'X-CSRF-Token': window.csrfToken,
'Content-Type': 'application/json',
},
credentials: 'same-origin',
})
.then((response) => response.json())
.then((content) => {
nextPage += 1;
insertArticles(content.result);
const checkBlockedContentEvent = new CustomEvent('checkBlockedContent');
window.dispatchEvent(checkBlockedContentEvent);
initializeReadingListIcons();
if (content.result.length === 0) {
document.getElementById('loading-articles').style.display = 'none';
done = true;
}
});
}
function fetchNextPageIfNearBottom() {
var indexContainer = document.getElementById('index-container');
var elCheck = indexContainer && !document.getElementById('query-wrapper');
if (!elCheck) {
return;
}
var indexWhich = indexContainer.dataset.which;
var fetchCallback;
var scrollableElemId;
if (indexWhich === 'podcast-episodes') {
scrollableElemId = 'articles-list';
fetchCallback = function fetch() {
fetchNextPodcastPage(indexContainer);
};
} else if (indexWhich === 'videos') {
scrollableElemId = 'video-collection';
fetchCallback = function fetch() {
fetchNextVideoPage(indexContainer);
};
} else if (indexWhich === 'followers') {
scrollableElemId = 'user-dashboard';
fetchCallback = function fetch() {
fetchNextFollowersPage(indexContainer);
};
} else if (indexWhich === 'following') {
scrollableElemId = 'user-dashboard';
fetchCallback = function fetch() {
fetchNextFollowingPage(indexContainer);
};
} else {
scrollableElemId = 'articles-list';
fetchCallback = function fetch() {
paginate(
indexContainer.dataset.tag,
indexContainer.dataset.params,
indexContainer.dataset.requiresApproval,
);
};
}
var scrollableElem = document.getElementById(scrollableElemId);
if (
!done &&
!fetching &&
window.scrollY > scrollableElem.scrollHeight - 3700
) {
fetching = true;
fetchCallback();
}
}
function checkIfNearBottomOfPage() {
if (
(document.getElementsByClassName('crayons-story').length < 2 &&
document.getElementsByClassName('single-article').length < 2) ||
window.location.search.indexOf('q=') > -1
) {
document.getElementById('loading-articles').style.display = 'none';
done = true;
} else {
document.getElementById('loading-articles').style.display = 'block';
}
fetchNextPageIfNearBottom();
setInterval(function handleInterval() {
fetchNextPageIfNearBottom();
}, 210);
}
function initScrolling() {
var elCheck = document.getElementById('index-container');
if (elCheck) {
initScrolling.called = true;
checkIfNearBottomOfPage();
}
}