Added prev/next post controls
This commit is contained in:
parent
33c1d99583
commit
688b5b1281
12 changed files with 200 additions and 9 deletions
4
TODO
4
TODO
|
@ -6,10 +6,6 @@ everything related to posts:
|
|||
- better thumbnail loading
|
||||
|
||||
- single post view
|
||||
- previous and next post (difficult)
|
||||
- remember last search
|
||||
- take care of pages
|
||||
- add A/D hotkeys
|
||||
- editing
|
||||
- ability to loop video posts
|
||||
|
||||
|
|
|
@ -9,6 +9,25 @@
|
|||
border: 0;
|
||||
}
|
||||
|
||||
#post-current-search-wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
#post-current-search {
|
||||
margin: 0 auto 1em auto;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#post-current-search a {
|
||||
margin: 0 2em;
|
||||
}
|
||||
#post-current-search a,
|
||||
#post-current-search div {
|
||||
display: inline-block;
|
||||
}
|
||||
#post-current-search a:not(.enabled) {
|
||||
color: silver;
|
||||
}
|
||||
|
||||
#post-view-wrapper #sidebar {
|
||||
line-height: 1.33em;
|
||||
font-size: 90%;
|
||||
|
|
|
@ -89,6 +89,8 @@
|
|||
<script type="text/javascript" src="/js/Controls/TagInput.js"></script>
|
||||
<script type="text/javascript" src="/js/PresenterManager.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/js/Services/PostsAroundCalculator.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/js/Presenters/TopNavigationPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/PagerPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/MessagePresenter.js"></script>
|
||||
|
|
|
@ -22,10 +22,15 @@ App.Keyboard = function(mousetrap) {
|
|||
mousetrap.reset();
|
||||
}
|
||||
|
||||
function unbind(key) {
|
||||
mousetrap.unbind(key);
|
||||
}
|
||||
|
||||
return {
|
||||
keydown: keydown,
|
||||
keyup: keyup,
|
||||
reset: reset,
|
||||
unbind: unbind,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ App.Pager = function(
|
|||
var pageNumber;
|
||||
var searchParams;
|
||||
var url;
|
||||
var cache = {};
|
||||
|
||||
function init(args) {
|
||||
url = args.url;
|
||||
|
@ -55,7 +56,7 @@ App.Pager = function(
|
|||
|
||||
function retrieve() {
|
||||
return promise.make(function(resolve, reject) {
|
||||
promise.wait(api.get(url, _.extend({}, searchParams, {page: pageNumber})))
|
||||
promise.wait(api.get(url, _.extend({page: pageNumber}, searchParams)))
|
||||
.then(function(response) {
|
||||
var pageSize = response.json.pageSize;
|
||||
var totalRecords = response.json.totalRecords;
|
||||
|
@ -71,6 +72,27 @@ App.Pager = function(
|
|||
});
|
||||
}
|
||||
|
||||
function retrieveCached() {
|
||||
return promise.make(function(resolve, reject) {
|
||||
var cacheKey = JSON.stringify(_.extend({}, searchParams, {url: url, page: getPage()}));
|
||||
if (cacheKey in cache) {
|
||||
resolve.apply(this, cache[cacheKey]);
|
||||
} else {
|
||||
promise.wait(retrieve())
|
||||
.then(function() {
|
||||
cache[cacheKey] = arguments;
|
||||
resolve.apply(this, arguments);
|
||||
}).fail(function() {
|
||||
reject.apply(this, arguments);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
cache = {};
|
||||
}
|
||||
|
||||
function getVisiblePages() {
|
||||
var pages = [1, totalPages || 1];
|
||||
var pagesAroundCurrent = 2;
|
||||
|
@ -100,7 +122,9 @@ App.Pager = function(
|
|||
getSearchParams: getSearchParams,
|
||||
setSearchParams: setSearchParams,
|
||||
retrieve: retrieve,
|
||||
retrieveCached: retrieveCached,
|
||||
getVisiblePages: getVisiblePages,
|
||||
resetCache: resetCache,
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@ App.Presenters.PostListPresenter = function(
|
|||
topNavigationPresenter.select('posts');
|
||||
topNavigationPresenter.changeTitle('Posts');
|
||||
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
|
||||
searchArgs.page = parseInt(searchArgs.page) || 1;
|
||||
|
||||
promise.wait(
|
||||
util.promiseTemplate('post-list'),
|
||||
|
@ -93,6 +94,7 @@ App.Presenters.PostListPresenter = function(
|
|||
|
||||
_.each(posts, function(post) {
|
||||
$target.append(jQuery('<li>' + itemTemplate({
|
||||
searchArgs: searchArgs,
|
||||
post: post,
|
||||
}) + '</li>'));
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ App.Presenters.PostPresenter = function(
|
|||
router,
|
||||
keyboard,
|
||||
presenterManager,
|
||||
postsAroundCalculator,
|
||||
postCommentListPresenter,
|
||||
topNavigationPresenter,
|
||||
messagePresenter) {
|
||||
|
@ -23,11 +24,13 @@ App.Presenters.PostPresenter = function(
|
|||
var postContentTemplate;
|
||||
var historyTemplate;
|
||||
|
||||
var postNameOrId;
|
||||
var searchArgs;
|
||||
|
||||
var post;
|
||||
var postScore;
|
||||
var postFavorites;
|
||||
var postHistory;
|
||||
var postNameOrId;
|
||||
|
||||
var privileges = {};
|
||||
var editPrivileges = {};
|
||||
|
@ -40,6 +43,7 @@ App.Presenters.PostPresenter = function(
|
|||
|
||||
function init(args, loaded) {
|
||||
topNavigationPresenter.select('posts');
|
||||
postsAroundCalculator.resetCache();
|
||||
|
||||
privileges.canDeletePosts = auth.hasPrivilege(auth.privileges.deletePosts);
|
||||
privileges.canFeaturePosts = auth.hasPrivilege(auth.privileges.featurePosts);
|
||||
|
@ -76,6 +80,9 @@ App.Presenters.PostPresenter = function(
|
|||
function reinit(args, loaded) {
|
||||
postNameOrId = args.postNameOrId;
|
||||
|
||||
searchArgs = util.parseComplexRouteArgs(args.searchArgs);
|
||||
searchArgs.page = parseInt(searchArgs.page) || 1;
|
||||
|
||||
promise.wait(refreshPost())
|
||||
.then(function() {
|
||||
topNavigationPresenter.changeTitle('@' + post.id);
|
||||
|
@ -84,6 +91,39 @@ App.Presenters.PostPresenter = function(
|
|||
}).fail(loaded);
|
||||
}
|
||||
|
||||
function attachLinksToPostsAround() {
|
||||
var searchParams = {query: searchArgs.query, order: searchArgs.order};
|
||||
promise.wait(postsAroundCalculator.getLinksToPostsAround(searchParams, searchArgs.page, post.id))
|
||||
.then(function(nextPostUrl, prevPostUrl) {
|
||||
var $prevPost = $el.find('#post-current-search .right a');
|
||||
var $nextPost = $el.find('#post-current-search .left a');
|
||||
|
||||
if (nextPostUrl) {
|
||||
$nextPost.addClass('enabled');
|
||||
$nextPost.attr('href', nextPostUrl);
|
||||
keyboard.keyup('a', function() {
|
||||
router.navigate(nextPostUrl);
|
||||
});
|
||||
} else {
|
||||
$nextPost.removeClass('enabled');
|
||||
$nextPost.removeAttr('href');
|
||||
keyboard.unbind('a');
|
||||
}
|
||||
|
||||
if (prevPostUrl) {
|
||||
$prevPost.addClass('enabled');
|
||||
$prevPost.attr('href', prevPostUrl);
|
||||
keyboard.keyup('d', function() {
|
||||
router.navigate(prevPostUrl);
|
||||
});
|
||||
} else {
|
||||
$prevPost.removeClass('enabled');
|
||||
$prevPost.removeAttr('href');
|
||||
keyboard.unbind('d');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function refreshPost() {
|
||||
return promise.make(function(resolve, reject) {
|
||||
promise.wait(api.get('/posts/' + postNameOrId))
|
||||
|
@ -128,6 +168,8 @@ App.Presenters.PostPresenter = function(
|
|||
presenterManager.initPresenters([
|
||||
[postCommentListPresenter, _.extend({post: post}, {$target: $el.find('#post-comments-target')})]],
|
||||
function() { });
|
||||
|
||||
attachLinksToPostsAround();
|
||||
}
|
||||
|
||||
function renderSidebar() {
|
||||
|
@ -137,6 +179,7 @@ App.Presenters.PostPresenter = function(
|
|||
|
||||
function renderPostTemplate() {
|
||||
return postTemplate({
|
||||
searchArgs: searchArgs,
|
||||
post: post,
|
||||
ownScore: postScore,
|
||||
postFavorites: postFavorites,
|
||||
|
@ -370,6 +413,7 @@ App.DI.register('postPresenter', [
|
|||
'router',
|
||||
'keyboard',
|
||||
'presenterManager',
|
||||
'postsAroundCalculator',
|
||||
'postCommentListPresenter',
|
||||
'topNavigationPresenter',
|
||||
'messagePresenter'],
|
||||
|
|
|
@ -47,6 +47,7 @@ App.Presenters.UserListPresenter = function(
|
|||
|
||||
var searchArgs = util.parseComplexRouteArgs(args.searchArgs);
|
||||
searchArgs.order = searchArgs.order || 'name,asc';
|
||||
searchArgs.page = parseInt(searchArgs.page) || 1;
|
||||
updateActiveOrder(searchArgs.order);
|
||||
|
||||
pagerPresenter.reinit({
|
||||
|
|
|
@ -39,7 +39,7 @@ App.Router = function(pathJs, _, jQuery, promise, util, appState, presenterManag
|
|||
inject('#/users(/:searchArgs)', 'userListPresenter');
|
||||
inject('#/user/:userName(/:tab)', 'userPresenter');
|
||||
inject('#/posts(/:searchArgs)', 'postListPresenter');
|
||||
inject('#/post(/:postNameOrId)', 'postPresenter');
|
||||
inject('#/post(/:postNameOrId)(/:searchArgs)', 'postPresenter');
|
||||
inject('#/comments(/:searchArgs)', 'globalCommentListPresenter');
|
||||
inject('#/tags(/:searchArgs)', 'tagListPresenter');
|
||||
inject('#/help', 'helpPresenter');
|
||||
|
|
70
public_html/js/Services/PostsAroundCalculator.js
Normal file
70
public_html/js/Services/PostsAroundCalculator.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
var App = App || {};
|
||||
App.Services = App.Services || {};
|
||||
|
||||
App.Services.PostsAroundCalculator = function(_, promise, util, pager) {
|
||||
|
||||
pager.init({url: '/posts'});
|
||||
|
||||
function resetCache() {
|
||||
pager.resetCache();
|
||||
}
|
||||
|
||||
function getLinksToPostsAround(searchParams, page, postId) {
|
||||
return promise.make(function(resolve, reject) {
|
||||
pager.setSearchParams(searchParams);
|
||||
pager.setPage(page);
|
||||
promise.wait(pager.retrieveCached())
|
||||
.then(function(response) {
|
||||
var postIds = _.pluck(response.entities, 'id');
|
||||
var position = _.indexOf(postIds, postId);
|
||||
|
||||
if (position === -1) {
|
||||
resolve(null, null);
|
||||
}
|
||||
|
||||
promise.wait(
|
||||
getLinkToPostAround(postIds, position, page, -1),
|
||||
getLinkToPostAround(postIds, position, page, 1))
|
||||
.then(function(nextPostUrl, prevPostUrl) {
|
||||
resolve(nextPostUrl, prevPostUrl);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getLinkToPostAround(postIds, position, page, direction) {
|
||||
return promise.make(function(resolve, reject) {
|
||||
if (position + direction >= 0 && position + direction < postIds.length) {
|
||||
var url = util.compileComplexRouteArgs(
|
||||
'#/post/' + postIds[position + direction],
|
||||
_.extend({page: page}, pager.getSearchParams()));
|
||||
resolve(url);
|
||||
} else if (page + direction >= 1) {
|
||||
pager.setPage(page + direction);
|
||||
promise.wait(pager.retrieveCached()).then(function(response) {
|
||||
if (response.entities.length) {
|
||||
var post = direction === - 1 ?
|
||||
_.last(response.entities) :
|
||||
_.first(response.entities);
|
||||
|
||||
var url = util.compileComplexRouteArgs(
|
||||
'#/post/' + post.id,
|
||||
_.extend({page: page + direction}, pager.getSearchParams()));
|
||||
resolve(url);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
resetCache: resetCache,
|
||||
getLinksToPostsAround: getLinksToPostsAround,
|
||||
};
|
||||
};
|
||||
|
||||
App.DI.register('postsAroundCalculator', ['_', 'promise', 'util', 'pager'], App.Services.PostsAroundCalculator);
|
|
@ -1,7 +1,11 @@
|
|||
<div class="post-small post-type-<%= post.contentType %> ">
|
||||
<a class="link"
|
||||
title="<%= _.map(post.tags, function(tag) { return '#' + tag.name; }).join(', ') %>"
|
||||
href="#/post/<%= post.id %>">
|
||||
<% if (typeof(searchArgs) !== 'undefined') { %>
|
||||
href="#/post/<%= post.id %>/query=<%= searchArgs.query %>;order=<%= searchArgs.order %>;page=<%= searchArgs.page %>"
|
||||
<% } else { %>
|
||||
href="#/post/<%= post.id %>"
|
||||
<% } %>
|
||||
title="<%= _.map(post.tags, function(tag) { return '#' + tag.name; }).join(', ') %>">
|
||||
|
||||
<img class="thumb" src="/data/thumbnails/160x160/posts/<%= post.name %>" alt="<%= post.idMarkdown %>"/>
|
||||
|
||||
|
|
|
@ -1,5 +1,29 @@
|
|||
<% var permaLink = (window.location.origin + '/' + window.location.pathname + '/data/posts/' + post.name).replace(/([^:])\/+/g, '$1/') %>
|
||||
|
||||
<div id="post-current-search-wrapper">
|
||||
<div id="post-current-search">
|
||||
<div class="left">
|
||||
<a class="enabled">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
Next
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="search">
|
||||
<a href="#/posts/query=<%= searchArgs.query %>;order=<%= searchArgs.order %>">
|
||||
Current search: <%= searchArgs.query || '-' %>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<a class="enabled">
|
||||
Previous
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="post-view-wrapper">
|
||||
<div id="sidebar">
|
||||
<ul class="essential">
|
||||
|
|
Loading…
Reference in a new issue