diff --git a/public/src/client/category.js b/public/src/client/category.js index 0dce7f869c..bea2ca0540 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -11,9 +11,8 @@ define('forum/category', [ 'components', 'translator', 'topicSelect', - 'forum/pagination', - 'storage', -], function (infinitescroll, share, navigator, categoryTools, recent, sort, components, translator, topicSelect, pagination, storage) { + 'handleBack', +], function (infinitescroll, share, navigator, categoryTools, recent, sort, components, translator, topicSelect, handleBack) { var Category = {}; $(window).on('action:ajaxify.start', function (ev, data) { @@ -43,15 +42,8 @@ define('forum/category', [ enableInfiniteLoadingOrPagination(); - $('[component="category"]').on('click', '[component="topic/header"]', function () { - var clickedIndex = $(this).parents('[data-index]').attr('data-index'); - $('[component="category/topic"]').each(function (index, el) { - if ($(el).offset().top - $(window).scrollTop() > 0) { - storage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index')); - storage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex); - return false; - } - }); + handleBack.init(function (after, cb) { + loadTopicsAfter(after, 1, cb); }); handleScrollToTopicIndex(); @@ -112,78 +104,6 @@ define('forum/category', [ return bottomIndex; }; - $(window).on('action:popstate', function () { - if (ajaxify.data.template.category && ajaxify.data.cid) { - var bookmarkIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark'); - var clickedIndex = storage.getItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); - - storage.removeItem('category:' + ajaxify.data.cid + ':bookmark'); - storage.removeItem('category:' + ajaxify.data.cid + ':bookmark:clicked'); - - bookmarkIndex = Math.max(0, parseInt(bookmarkIndex, 10) || 0); - clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0); - if (!parseInt(bookmarkIndex, 10)) { - return; - } - - if (config.usePagination) { - var page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage); - if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { - pagination.loadPage(page, function () { - Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); - }); - } else { - Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); - } - } else { - if (bookmarkIndex === 0) { - Category.highlightTopic(clickedIndex); - return; - } - - $('[component="category"]').empty(); - - loadTopicsAfter(Math.max(0, bookmarkIndex - 1) + 1, 1, function () { - $(window).one('action:topics.loaded', function () { - Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); - }); - }); - } - } - }); - - Category.highlightTopic = function (topicIndex) { - var highlight = components.get('category/topic', 'index', topicIndex); - - if (highlight.length && !highlight.hasClass('highlight')) { - highlight.addClass('highlight'); - setTimeout(function () { - highlight.removeClass('highlight'); - }, 5000); - } - }; - - Category.scrollToTopic = function (bookmarkIndex, clickedIndex, duration, offset) { - if (!bookmarkIndex) { - return; - } - - if (!offset) { - offset = 0; - } - - var scrollTo = components.get('category/topic', 'index', bookmarkIndex); - - if (scrollTo.length) { - $('html, body').animate({ - scrollTop: (scrollTo.offset().top - offset) + 'px', - }, duration !== undefined ? duration : 400, function () { - Category.highlightTopic(clickedIndex); - navigator.update(); - }); - } - }; - function enableInfiniteLoadingOrPagination() { if (!config.usePagination) { navigator.init('[component="category/topic"]', ajaxify.data.topic_count, Category.toTop, Category.toBottom, Category.navigatorCallback); diff --git a/public/src/client/popular.js b/public/src/client/popular.js index b63f776bff..c94b8d912f 100644 --- a/public/src/client/popular.js +++ b/public/src/client/popular.js @@ -28,7 +28,7 @@ define('forum/popular', ['forum/recent', 'components', 'forum/infinitescroll'], term: ajaxify.data.term, }, function (data, done) { if (data.topics && data.topics.length) { - recent.onTopicsLoaded('popular', data.topics, false, done); + recent.onTopicsLoaded('popular', data.topics, false, direction, done); $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 55ae67500f..71342661df 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -1,7 +1,6 @@ 'use strict'; - -define('forum/recent', ['forum/infinitescroll', 'components'], function (infinitescroll, components) { +define('forum/recent', ['forum/infinitescroll', 'components', 'handleBack'], function (infinitescroll, components, handleBack) { var Recent = {}; var newTopicCount = 0; @@ -20,6 +19,10 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit Recent.handleCategorySelection(); + handleBack.init(function (after, cb) { + loadTopicsAfter(after, 1, cb); + }); + $('#new-topics-alert').on('click', function () { $(this).addClass('hide'); }); @@ -186,27 +189,36 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit }; Recent.loadMoreTopics = function (direction) { - if (direction < 0 || !$('[component="category"]').length) { + if (!$('[component="category"]').length) { return; } + var topics = $('[component="category/topic"]'); + var afterEl = direction > 0 ? topics.last() : topics.first(); + var after = (parseInt(afterEl.attr('data-index'), 10) || 0) + (direction > 0 ? 1 : 0); + loadTopicsAfter(after, direction); + }; + function loadTopicsAfter(after, direction, callback) { + callback = callback || function () {}; infinitescroll.loadMore('topics.loadMoreRecentTopics', { - after: $('[component="category"]').attr('data-nextstart'), + after: after, + direction: direction, count: config.topicsPerPage, cid: utils.params().cid, filter: ajaxify.data.selectedFilter.filter, set: $('[component="category"]').attr('data-set') ? $('[component="category"]').attr('data-set') : 'topics:recent', }, function (data, done) { if (data.topics && data.topics.length) { - Recent.onTopicsLoaded('recent', data.topics, false, done); + Recent.onTopicsLoaded('recent', data.topics, false, direction, done); } else { done(); } $('[component="category"]').attr('data-nextstart', data.nextStart); + callback(); }); - }; + } - Recent.onTopicsLoaded = function (templateName, topics, showSelect, callback) { + Recent.onTopicsLoaded = function (templateName, topics, showSelect, direction, callback) { topics = topics.filter(function (topic) { return !components.get('category/topic', 'tid', topic.tid).length; }); @@ -215,10 +227,32 @@ define('forum/recent', ['forum/infinitescroll', 'components'], function (infinit return callback(); } + var after; + var before; + var topicsList = $('[component="category/topic"]'); + + if (direction > 0 && topics.length) { + after = topicsList.last(); + } else if (direction < 0 && topics.length) { + before = topicsList.first(); + } + app.parseAndTranslate(templateName, 'topics', { topics: topics, showSelect: showSelect }, function (html) { $('#category-no-topics').remove(); - html.insertAfter($('[component="category/topic"]').last()); + if (after && after.length) { + html.insertAfter(after); + } else if (before && before.length) { + var height = $(document).height(); + var scrollTop = $(window).scrollTop(); + + html.insertBefore(before); + + $(window).scrollTop(scrollTop + ($(document).height() - height)); + } else { + $('[component="category"]').append(html); + } + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); diff --git a/public/src/client/tag.js b/public/src/client/tag.js index e07126cb2e..53867fd5b7 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -30,7 +30,7 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function (recent, count: config.topicsPerPage, }, function (data, done) { if (data.topics && data.topics.length) { - recent.onTopicsLoaded('tag', data.topics, false, done); + recent.onTopicsLoaded('tag', data.topics, false, direction, done); } else { done(); $('#load-more-btn').hide(); diff --git a/public/src/client/top.js b/public/src/client/top.js index 9e80cb668a..e0c975fdea 100644 --- a/public/src/client/top.js +++ b/public/src/client/top.js @@ -39,7 +39,7 @@ define('forum/top', ['forum/recent', 'forum/infinitescroll'], function (recent, filter: ajaxify.data.selectedFilter.filter, }, function (data, done) { if (data.topics && data.topics.length) { - recent.onTopicsLoaded('top', data.topics, true, done); + recent.onTopicsLoaded('top', data.topics, true, direction, done); $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 9b6ddea7a0..1f1c821ae6 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -98,7 +98,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll', ' filter: ajaxify.data.selectedFilter.filter, }, function (data, done) { if (data.topics && data.topics.length) { - recent.onTopicsLoaded('unread', data.topics, true, done); + recent.onTopicsLoaded('unread', data.topics, true, direction, done); $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); diff --git a/public/src/modules/handleBack.js b/public/src/modules/handleBack.js new file mode 100644 index 0000000000..6a11ebc45e --- /dev/null +++ b/public/src/modules/handleBack.js @@ -0,0 +1,103 @@ +'use strict'; + +define('handleBack', [ + 'components', + 'storage', + 'navigator', + 'forum/pagination', +], function (components, storage, navigator, pagination) { + var handleBack = {}; + var loadTopicsMethod; + + handleBack.init = function (_loadTopicsMethod) { + loadTopicsMethod = _loadTopicsMethod; + saveClickedIndex(); + $(window).off('action:popstate', onBackClicked).on('action:popstate', onBackClicked); + }; + + function saveClickedIndex() { + $('[component="category"]').on('click', '[component="topic/header"]', function () { + var clickedIndex = $(this).parents('[data-index]').attr('data-index'); + $('[component="category/topic"]').each(function (index, el) { + if ($(el).offset().top - $(window).scrollTop() > 0) { + storage.setItem('category:bookmark', $(el).attr('data-index')); + storage.setItem('category:bookmark:clicked', clickedIndex); + return false; + } + }); + }); + } + + function onBackClicked() { + if ((ajaxify.data.template.category || ajaxify.data.template.recent)) { + var bookmarkIndex = storage.getItem('category:bookmark'); + var clickedIndex = storage.getItem('category:bookmark:clicked'); + + storage.removeItem('category:bookmark'); + storage.removeItem('category:bookmark:clicked'); + + bookmarkIndex = Math.max(0, parseInt(bookmarkIndex, 10) || 0); + clickedIndex = Math.max(0, parseInt(clickedIndex, 10) || 0); + if (!utils.isNumber(bookmarkIndex)) { + return; + } + + if (config.usePagination) { + var page = Math.ceil((parseInt(bookmarkIndex, 10) + 1) / config.topicsPerPage); + if (parseInt(page, 10) !== ajaxify.data.pagination.currentPage) { + pagination.loadPage(page, function () { + handleBack.scrollToTopic(bookmarkIndex, clickedIndex, 0); + }); + } else { + handleBack.scrollToTopic(bookmarkIndex, clickedIndex, 0); + } + } else { + if (bookmarkIndex === 0) { + handleBack.highlightTopic(clickedIndex); + return; + } + + $('[component="category"]').empty(); + loadTopicsMethod(Math.max(0, bookmarkIndex - 1) + 1, function () { + $(window).one('action:topics.loaded', function () { + handleBack.scrollToTopic(bookmarkIndex, clickedIndex, 0); + }); + }); + } + } + } + + handleBack.highlightTopic = function (topicIndex) { + var highlight = components.get('category/topic', 'index', topicIndex); + + if (highlight.length && !highlight.hasClass('highlight')) { + highlight.addClass('highlight'); + setTimeout(function () { + highlight.removeClass('highlight'); + }, 5000); + } + }; + + handleBack.scrollToTopic = function (bookmarkIndex, clickedIndex, duration, offset) { + if (!utils.isNumber(bookmarkIndex)) { + return; + } + + if (!offset) { + offset = 0; + } + + var scrollTo = components.get('category/topic', 'index', bookmarkIndex); + + if (scrollTo.length) { + $('html, body').animate({ + scrollTop: (scrollTo.offset().top - offset) + 'px', + }, duration !== undefined ? duration : 400, function () { + handleBack.highlightTopic(clickedIndex); + navigator.update(); + }); + } + }; + + return handleBack; +}); diff --git a/src/meta/js.js b/src/meta/js.js index c2ccdabedf..4e974c74e8 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -75,6 +75,7 @@ JS.scripts = { 'public/src/modules/helpers.js', 'public/src/modules/flags.js', 'public/src/modules/storage.js', + 'public/src/modules/handleBack.js', ], admin: [ diff --git a/src/socket.io/topics/infinitescroll.js b/src/socket.io/topics/infinitescroll.js index 64fa087430..343bef113b 100644 --- a/src/socket.io/topics/infinitescroll.js +++ b/src/socket.io/topics/infinitescroll.js @@ -115,8 +115,14 @@ module.exports = function (SocketTopics) { if (!data || !utils.isNumber(data.after) || parseInt(data.after, 10) < 0) { return callback(new Error('[[error:invalid-data]]')); } - var start = parseInt(data.after, 10); - var stop = start + Math.max(0, Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20) - 1); + var itemsPerPage = Math.min(meta.config.topicsPerPage || 20, parseInt(data.count, 10) || meta.config.topicsPerPage || 20); + var start = Math.max(0, parseInt(data.after, 10)); + if (data.direction === -1) { + start -= itemsPerPage; + } + var stop = start + Math.max(0, itemsPerPage - 1); + start = Math.max(0, start); + stop = Math.max(0, stop); loadFn(start, stop); } diff --git a/src/topics/recent.js b/src/topics/recent.js index 30da085deb..5b717d9639 100644 --- a/src/topics/recent.js +++ b/src/topics/recent.js @@ -45,6 +45,9 @@ module.exports = function (Topics) { Topics.getTopicsByTids(tids, uid, next); }, function (topicData, next) { + topicData.forEach(function (topicObj, i) { + topicObj.index = start + i; + }); recentTopics.topics = topicData; recentTopics.nextStart = stop + 1; next(null, recentTopics);