mirror of
https://github.com/NodeBB/NodeBB.git
synced 2026-06-15 22:11:17 +02:00
refactor: remove glance
assorted fixes to navigator dont reduce remaning count if user scrolls down and up quickly only call topic.navigatorCallback when index changes
This commit is contained in:
@@ -30,7 +30,7 @@ define('forum/category', [
|
||||
sort.handleSort('categoryTopicSort', 'category/' + ajaxify.data.slug);
|
||||
|
||||
if (!config.usePagination) {
|
||||
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback);
|
||||
navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom);
|
||||
} else {
|
||||
navigator.disable();
|
||||
}
|
||||
@@ -129,10 +129,6 @@ define('forum/category', [
|
||||
});
|
||||
};
|
||||
|
||||
Category.navigatorCallback = function (topIndex, bottomIndex) {
|
||||
return bottomIndex;
|
||||
};
|
||||
|
||||
function loadTopicsAfter(after, direction, callback) {
|
||||
callback = callback || function () {};
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ define('forum/topic', [
|
||||
'forum/topic/postTools',
|
||||
'forum/topic/events',
|
||||
'forum/topic/posts',
|
||||
'forum/topic/glance',
|
||||
'navigator',
|
||||
'sort',
|
||||
'quickreply',
|
||||
@@ -18,7 +17,7 @@ define('forum/topic', [
|
||||
'alerts',
|
||||
], function (
|
||||
infinitescroll, threadTools, postTools,
|
||||
events, posts, glance, navigator, sort, quickreply,
|
||||
events, posts, navigator, sort, quickreply,
|
||||
components, storage, hooks, api, alerts
|
||||
) {
|
||||
const Topic = {};
|
||||
@@ -47,7 +46,7 @@ define('forum/topic', [
|
||||
posts.signaturesShown = {};
|
||||
}
|
||||
await posts.onTopicPageLoad(components.get('post'));
|
||||
navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, utils.debounce(Topic.navigatorCallback, 500));
|
||||
navigator.init('[component="post"]', ajaxify.data.postcount, Topic.toTop, Topic.toBottom, Topic.navigatorCallback);
|
||||
|
||||
postTools.init(tid);
|
||||
threadTools.init(tid, $('.topic'));
|
||||
@@ -66,7 +65,6 @@ define('forum/topic', [
|
||||
addPostsPreviewHandler();
|
||||
setupQuickReply();
|
||||
handleBookmark(tid);
|
||||
glance.default();
|
||||
|
||||
$(window).on('scroll', utils.debounce(updateTopicTitle, 250));
|
||||
|
||||
@@ -329,7 +327,6 @@ define('forum/topic', [
|
||||
|
||||
updateUserBookmark(index);
|
||||
|
||||
Topic.replaceURLTimeout = 0;
|
||||
if (ajaxify.data.updateUrlWithPostIndex && history.replaceState) {
|
||||
let search = window.location.search || '';
|
||||
if (!config.usePagination) {
|
||||
|
||||
@@ -1,296 +0,0 @@
|
||||
/* eslint-disable import/no-unresolved */
|
||||
|
||||
import { render } from 'benchpress';
|
||||
import { loadMore } from 'forum/infinitescroll';
|
||||
import * as navigator from 'navigator';
|
||||
import { onNewPostsAddedToDom } from 'forum/topic/posts';
|
||||
import { onPage, one as once } from 'hooks';
|
||||
import { translate } from 'translator';
|
||||
|
||||
let trackTop;
|
||||
let trackBottom;
|
||||
let trackHeight;
|
||||
let handleEl;
|
||||
|
||||
export default function init() {
|
||||
const topicEl = document.querySelector('[component="topic"]');
|
||||
const navigatorEl = document.querySelector('[component="topic/navigator"]');
|
||||
|
||||
if (!ajaxify.data.template.topic || !topicEl || !navigatorEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
enableButtons();
|
||||
({ handleEl } = enableHandle());
|
||||
updateHandleText(ajaxify.data.postIndex);
|
||||
updateUnreadIndicator(ajaxify.data.postIndex);
|
||||
|
||||
once('action:ajaxify.cleanup', () => {
|
||||
window.removeEventListener('resize', updateTrackPosition);
|
||||
});
|
||||
|
||||
console.debug('[glance] At-a-glance navigator enabled.');
|
||||
}
|
||||
|
||||
function updateTrackPosition() {
|
||||
const trackEl = document.querySelector('[component="topic/navigator"] .track');
|
||||
({ top: trackTop, bottom: trackBottom, height: trackHeight } = trackEl.getBoundingClientRect());
|
||||
}
|
||||
|
||||
function enableButtons() {
|
||||
const navigatorEl = document.querySelector('[component="topic/navigator"]');
|
||||
navigatorEl.addEventListener('click', (e) => {
|
||||
const subselector = e.target.closest('[data-action]');
|
||||
if (!subselector) {
|
||||
return;
|
||||
}
|
||||
|
||||
const action = subselector.getAttribute('data-action');
|
||||
navigator[action]();
|
||||
});
|
||||
}
|
||||
|
||||
function enableHandle() {
|
||||
const handleEl = document.querySelector('[component="topic/navigator"] .handle');
|
||||
let active = false;
|
||||
|
||||
updateTrackPosition();
|
||||
window.addEventListener('resize', updateTrackPosition);
|
||||
|
||||
onPage('action:navigator.update', utils.debounce(({ newIndex }) => {
|
||||
if (!active) {
|
||||
repositionHandle(newIndex);
|
||||
}
|
||||
}, 150));
|
||||
|
||||
onPage('action:navigator.scrolled', ({ newIndex }) => {
|
||||
if (!active) {
|
||||
repositionHandle(newIndex);
|
||||
}
|
||||
});
|
||||
|
||||
handleEl.addEventListener('mousedown', (e) => {
|
||||
// Only respond to left click
|
||||
if (e.buttons !== 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tUpdateHandleText = utils.throttle(() => {
|
||||
updateHandleText(getIndexFromTrack());
|
||||
}, 250);
|
||||
|
||||
toggle(true);
|
||||
active = true;
|
||||
document.addEventListener('mousemove', onHandleMove);
|
||||
document.addEventListener('mousemove', tUpdateHandleText);
|
||||
document.addEventListener('mouseup', () => {
|
||||
toggle(false);
|
||||
document.removeEventListener('mousemove', onHandleMove);
|
||||
document.removeEventListener('mousemove', tUpdateHandleText);
|
||||
active = false;
|
||||
}, {
|
||||
once: true,
|
||||
});
|
||||
});
|
||||
|
||||
return { handleEl };
|
||||
}
|
||||
|
||||
function getIndexFromTrack() {
|
||||
const { top: handleTop } = handleEl.getBoundingClientRect();
|
||||
const delta = handleTop - trackTop;
|
||||
const percentage = trackHeight > 0 ? delta / trackHeight : 0;
|
||||
const index = Math.floor(ajaxify.data.postcount * percentage);
|
||||
return index;
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/21696585/583363
|
||||
const isHidden = el => el.offsetParent === null;
|
||||
|
||||
|
||||
async function updateUnreadIndicator(index) {
|
||||
if (ajaxify.data.postcount <= ajaxify.data.bookmarkThreshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
index = Math.max(index, ajaxify.data.bookmark);
|
||||
const unreadEl = document.querySelector('[component="topic/navigator"] .unread');
|
||||
const meta = unreadEl.querySelector('.meta');
|
||||
if (isHidden(meta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const percentage = 1 - (index / ajaxify.data.postcount);
|
||||
unreadEl.style.height = `${trackHeight * percentage}px`;
|
||||
|
||||
const anchorEl = unreadEl.querySelector('.meta a');
|
||||
const remaining = ajaxify.data.postcount - index;
|
||||
if (remaining > 0) {
|
||||
const text = await translate(`[[topic:navigator.unread, ${remaining}]]`);
|
||||
anchorEl.href = `${config.relative_path}/topic/${ajaxify.data.slug}/${Math.min(index + 1, ajaxify.data.postcount)}`;
|
||||
anchorEl.innerText = text;
|
||||
} else {
|
||||
anchorEl.href = ajaxify.data.url;
|
||||
anchorEl.innerText = '';
|
||||
}
|
||||
}
|
||||
|
||||
function repositionHandle(index) {
|
||||
const percentage = Math.max(0, (index - 1) / ajaxify.data.postcount);
|
||||
handleEl.style.top = `${trackHeight * percentage}px`;
|
||||
updateHandleText(index);
|
||||
updateUnreadIndicator(index);
|
||||
}
|
||||
|
||||
async function updateHandleText(index) {
|
||||
const { tid } = ajaxify.data;
|
||||
const meta = handleEl.querySelector('.meta');
|
||||
if (isHidden(meta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const indexEl = meta.querySelector('.index');
|
||||
const timestampEl = meta.querySelector('.timestamp');
|
||||
|
||||
const text = await translate(`[[topic:navigator.index, ${index}, ${ajaxify.data.postcount}]]`);
|
||||
indexEl.innerText = text;
|
||||
|
||||
const { timestampISO } = await socket.emit('posts.getPostSummaryByIndex', { tid, index: index - 1 });
|
||||
timestampEl.title = timestampISO;
|
||||
$(timestampEl).timeago();
|
||||
}
|
||||
|
||||
function onHandleMove(e) {
|
||||
const top = Math.min(trackBottom, Math.max(trackTop, e.clientY)) - trackTop;
|
||||
const percentage = top / trackHeight;
|
||||
|
||||
const documentHeight = document.documentElement.scrollHeight - window.innerHeight;
|
||||
|
||||
handleEl.style.top = `${top}px`;
|
||||
window.scrollTo(0, documentHeight * percentage);
|
||||
}
|
||||
|
||||
function toggle(state) {
|
||||
const topicEl = document.querySelector('[component="topic"]');
|
||||
|
||||
if (state === undefined) {
|
||||
state = app.flags._glance !== true;
|
||||
}
|
||||
|
||||
topicEl.classList[state ? 'add' : 'remove']('minimal');
|
||||
|
||||
if (state) {
|
||||
app.flags._glance = true;
|
||||
generatePlaceholders();
|
||||
registerScrollEvent();
|
||||
} else {
|
||||
removePlaceholders();
|
||||
deregisterScrollEvent();
|
||||
|
||||
navigator.scrollToIndex(getIndexFromTrack(), true, 0);
|
||||
delete app.flags._glance;
|
||||
}
|
||||
}
|
||||
|
||||
let ticking = false;
|
||||
let scrollTimeout;
|
||||
function onScrollTick() {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(() => {
|
||||
if (scrollTimeout) {
|
||||
clearTimeout(scrollTimeout);
|
||||
}
|
||||
scrollTimeout = setTimeout(onScrollEnd, 500);
|
||||
ticking = false;
|
||||
});
|
||||
|
||||
ticking = true;
|
||||
}
|
||||
}
|
||||
|
||||
async function onScrollEnd() {
|
||||
const placeholders = Array.from(document.querySelectorAll('[component="post/placeholder"]')).filter((el) => {
|
||||
const { top, bottom } = el.getBoundingClientRect();
|
||||
return bottom > 0 && top < window.innerHeight;
|
||||
});
|
||||
|
||||
if (!placeholders.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstIndex = placeholders[0].getAttribute('data-index');
|
||||
|
||||
const { data, done } = await new Promise((resolve) => {
|
||||
loadMore('topics.loadMore', {
|
||||
tid: ajaxify.data.tid,
|
||||
after: firstIndex, // + (direction > 0 ? 1 : 0),
|
||||
count: placeholders.length,
|
||||
direction: 1,
|
||||
topicPostSort: config.topicPostSort,
|
||||
}, function (data, done) {
|
||||
resolve({ data, done });
|
||||
});
|
||||
});
|
||||
|
||||
let jqueryElements = await app.parseAndTranslate('topic', 'posts', data);
|
||||
jqueryElements = jqueryElements.filter((i, e) => e.nodeType === 1);
|
||||
jqueryElements.each((i, el) => {
|
||||
const index = el.getAttribute('data-index');
|
||||
const placeholderEl = document.querySelector(`[component="post/placeholder"][data-index="${index}"]`);
|
||||
if (!placeholderEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
placeholderEl.replaceWith(el);
|
||||
});
|
||||
await onNewPostsAddedToDom(jqueryElements);
|
||||
done();
|
||||
}
|
||||
|
||||
function registerScrollEvent() {
|
||||
document.addEventListener('scroll', onScrollTick);
|
||||
}
|
||||
|
||||
function deregisterScrollEvent() {
|
||||
document.removeEventListener('scroll', onScrollTick);
|
||||
}
|
||||
|
||||
async function generatePlaceholders() {
|
||||
const { postcount } = ajaxify.data;
|
||||
const posts = document.querySelectorAll('[component="post"]');
|
||||
if (!posts.length) {
|
||||
throw new Error('[[error:no-post]]');
|
||||
}
|
||||
|
||||
const firstPost = posts[0];
|
||||
const lastPost = posts[posts.length - 1];
|
||||
const firstIndex = parseInt(firstPost.getAttribute('data-index'), 10);
|
||||
const lastIndex = parseInt(lastPost.getAttribute('data-index'), 10);
|
||||
|
||||
const numAbove = firstIndex;
|
||||
const numBelow = postcount - lastIndex - 1;
|
||||
|
||||
const placeholderEl = document.createElement('li');
|
||||
const html = await render('partials/topic/post-placeholder', {});
|
||||
placeholderEl.classList.add('pt-4'); // harmony-specific
|
||||
placeholderEl.setAttribute('component', 'post/placeholder');
|
||||
|
||||
const postsEl = document.querySelector('[component="topic"]');
|
||||
for (let x = 0, index = firstIndex; x < numAbove; x++, index--) {
|
||||
const node = placeholderEl.cloneNode();
|
||||
node.setAttribute('data-index', index - 1);
|
||||
node.innerHTML = html;
|
||||
postsEl.prepend(node);
|
||||
}
|
||||
for (let x = 0, index = lastIndex; x < numBelow; x++, index++) {
|
||||
const node = placeholderEl.cloneNode();
|
||||
node.setAttribute('data-index', index + 1);
|
||||
node.innerHTML = html;
|
||||
postsEl.append(node);
|
||||
}
|
||||
}
|
||||
|
||||
function removePlaceholders() {
|
||||
// todo: directionality
|
||||
document.querySelectorAll('[component="post/placeholder"]').forEach(el => el.remove());
|
||||
}
|
||||
@@ -4,6 +4,7 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
const navigator = {};
|
||||
let index = 0;
|
||||
let count = 0;
|
||||
let remaining = 0;
|
||||
let navigatorUpdateTimeoutId;
|
||||
|
||||
let renderPostIntervalId;
|
||||
@@ -86,13 +87,14 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
|
||||
if (ajaxify.data.template.topic) {
|
||||
handleScrollNav();
|
||||
remaining = ajaxify.data.postcount;
|
||||
updateUnreadIndicator(ajaxify.data.postIndex);
|
||||
}
|
||||
|
||||
handleKeys();
|
||||
|
||||
navigator.setCount(count);
|
||||
navigator.update(0);
|
||||
navigator.update();
|
||||
};
|
||||
|
||||
let lastNextIndex = 0;
|
||||
@@ -164,6 +166,7 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
updateThumbTimestampToIndex(thumb, index);
|
||||
});
|
||||
|
||||
updateUnreadIndicator(index);
|
||||
renderPost(index);
|
||||
}
|
||||
|
||||
@@ -361,10 +364,12 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
unreadEl.style.height = `${trackHeight * percentage}px`;
|
||||
|
||||
const thumbEl = trackEl.querySelector('.scroller-thumb');
|
||||
const thumbBottom = parseInt(thumbEl.style.top || 0, 10) + parseInt(thumbEl.style.height, 10);
|
||||
const thumbHeight = parseInt(thumbEl.style.height, 10);
|
||||
const thumbBottom = parseInt(thumbEl.style.top || 0, 10) + thumbHeight;
|
||||
const anchorEl = unreadEl.querySelector('.meta a');
|
||||
const remaining = ajaxify.data.postcount - index;
|
||||
if (remaining > 0 && (trackHeight - thumbBottom) > 50) {
|
||||
remaining = Math.min(remaining, ajaxify.data.postcount - index);
|
||||
|
||||
if (remaining > 0 && (trackHeight - thumbBottom) >= thumbHeight) {
|
||||
const text = await translator.translate(`[[topic:navigator.unread, ${remaining}]]`);
|
||||
anchorEl.href = `${config.relative_path}/topic/${ajaxify.data.slug}/${Math.min(index + 1, ajaxify.data.postcount)}`;
|
||||
anchorEl.innerText = text;
|
||||
@@ -503,16 +508,14 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
newIndex = count;
|
||||
}
|
||||
|
||||
if (typeof navigator.callback === 'function') {
|
||||
navigator.callback(newIndex, count);
|
||||
}
|
||||
|
||||
hooks.fire('action:navigator.update', { newIndex });
|
||||
hooks.fire('action:navigator.update', { newIndex, index });
|
||||
|
||||
if (newIndex !== index) {
|
||||
if (typeof navigator.callback === 'function') {
|
||||
navigator.callback(newIndex, count);
|
||||
}
|
||||
index = newIndex;
|
||||
navigator.updateTextAndProgressBar();
|
||||
updateUnreadIndicator(index);
|
||||
setThumbToIndex(index);
|
||||
}
|
||||
|
||||
@@ -687,9 +690,8 @@ define('navigator', ['forum/pagination', 'components', 'hooks', 'alerts', 'trans
|
||||
navigator.scrollActive = false;
|
||||
highlightPost();
|
||||
|
||||
const scrollToRect = scrollTo.get(0).getBoundingClientRect();
|
||||
if (!newIndex) {
|
||||
navigator.update(scrollToRect.top);
|
||||
navigator.update();
|
||||
} else {
|
||||
navigator.setIndex(newIndex);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user