From 54505e7a472bc440b101de73462eb43c9eca20cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Wed, 15 Feb 2023 21:28:37 -0500 Subject: [PATCH] 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 --- public/src/client/category.js | 6 +- public/src/client/topic.js | 7 +- public/src/client/topic/glance.js | 296 ------------------------------ public/src/modules/navigator.js | 26 +-- 4 files changed, 17 insertions(+), 318 deletions(-) delete mode 100644 public/src/client/topic/glance.js diff --git a/public/src/client/category.js b/public/src/client/category.js index 7a2a651368..991f4eb85e 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -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 () {}; diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 9150414dec..bc0dd13017 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -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) { diff --git a/public/src/client/topic/glance.js b/public/src/client/topic/glance.js deleted file mode 100644 index c700f2d022..0000000000 --- a/public/src/client/topic/glance.js +++ /dev/null @@ -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()); -} diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index 365d92dc4f..18a4446548 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -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); }