mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
feat: show posts previews if enabled on mouse over
This commit is contained in:
@@ -69,6 +69,7 @@
|
||||
"gdpr_enabled": 1,
|
||||
"allowProfileImageUploads": 1,
|
||||
"teaserPost": "last-reply",
|
||||
"showPostPreviewsOnHover": 1,
|
||||
"allowPrivateGroups": 1,
|
||||
"unreadCutoff": 2,
|
||||
"bookmarkThreshold": 5,
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"teaser.last-post": "Last – Show the latest post, including the original post, if no replies",
|
||||
"teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies",
|
||||
"teaser.first": "First",
|
||||
"showPostPreviewsOnHover": "Show a preview of posts when mouse overed",
|
||||
"unread": "Unread Settings",
|
||||
"unread.cutoff": "Unread cutoff days",
|
||||
"unread.min-track-last": "Minimum posts in topic before tracking last read",
|
||||
|
||||
@@ -12,10 +12,11 @@ define('forum/topic', [
|
||||
'components',
|
||||
'storage',
|
||||
'hooks',
|
||||
'api',
|
||||
], function (
|
||||
infinitescroll, threadTools, postTools,
|
||||
events, posts, navigator, sort,
|
||||
components, storage, hooks
|
||||
components, storage, hooks, api
|
||||
) {
|
||||
const Topic = {};
|
||||
let currentUrl = '';
|
||||
@@ -55,6 +56,7 @@ define('forum/topic', [
|
||||
addParentHandler();
|
||||
addDropupHandler();
|
||||
addRepliesHandler();
|
||||
addPostsPreviewHandler();
|
||||
|
||||
handleBookmark(tid);
|
||||
|
||||
@@ -172,6 +174,59 @@ define('forum/topic', [
|
||||
});
|
||||
}
|
||||
|
||||
function addPostsPreviewHandler() {
|
||||
if (!ajaxify.data.showPostPreviewsOnHover) {
|
||||
return;
|
||||
}
|
||||
let timeoutId = 0;
|
||||
$('[component="topic"]').on('mouseenter', '[component="post"] a, [component="topic/event"] a', async function () {
|
||||
const link = $(this);
|
||||
|
||||
async function renderPost(pid) {
|
||||
const postData = await socket.emit('posts.getPostSummaryByPid', { pid: pid });
|
||||
if (postData) {
|
||||
const tooltip = await app.parseAndTranslate('partials/topic/post-preview', { post: postData });
|
||||
tooltip.hide().find('.timeago').timeago();
|
||||
tooltip.appendTo($('body')).fadeIn(300);
|
||||
const postContent = link.parents('[component="topic"]').find('[component="post/content"]').first();
|
||||
const postRect = postContent.offset();
|
||||
const postWidth = postContent.width();
|
||||
const linkRect = link.offset();
|
||||
tooltip.css({
|
||||
top: linkRect.top + 30,
|
||||
left: postRect.left,
|
||||
width: postWidth,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const href = link.attr('href');
|
||||
const pathname = utils.urlToLocation(href).pathname;
|
||||
$('#post-tooltip').remove();
|
||||
const postMatch = pathname && pathname.match(/\/post\/([\d]+)/);
|
||||
const topicMatch = pathname && pathname.match(/\/topic\/([\d]+)/);
|
||||
if (postMatch) {
|
||||
const pid = postMatch[1];
|
||||
if (parseInt(link.parents('[component="post"]').attr('data-pid'), 10) === parseInt(pid, 10)) {
|
||||
return; // dont render self post
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(async () => {
|
||||
renderPost(pid);
|
||||
}, 300);
|
||||
} else if (topicMatch) {
|
||||
timeoutId = setTimeout(async () => {
|
||||
const tid = topicMatch[1];
|
||||
const topicData = await api.get('/topics/' + tid, {});
|
||||
renderPost(topicData.mainPid);
|
||||
}, 300);
|
||||
}
|
||||
}).on('mouseleave', '[component="post"] a, [component="topic/event"] a', function () {
|
||||
clearTimeout(timeoutId);
|
||||
$('#post-tooltip').remove();
|
||||
});
|
||||
}
|
||||
|
||||
function updateTopicTitle() {
|
||||
const span = components.get('navbar/title').find('span');
|
||||
if ($(window).scrollTop() > 50 && span.hasClass('hidden')) {
|
||||
|
||||
@@ -96,6 +96,7 @@ topicsController.get = async function getTopic(req, res, next) {
|
||||
topicData.updateUrlWithPostIndex = settings.updateUrlWithPostIndex;
|
||||
topicData.allowMultipleBadges = meta.config.allowMultipleBadges === 1;
|
||||
topicData.privateUploads = meta.config.privateUploads === 1;
|
||||
topicData.showPostPreviewsOnHover = meta.config.showPostPreviewsOnHover === 1;
|
||||
topicData.rssFeedUrl = `${relative_path}/topic/${topicData.tid}.rss`;
|
||||
if (req.loggedIn) {
|
||||
topicData.rssFeedUrl += `?uid=${req.uid}&token=${rssToken}`;
|
||||
|
||||
@@ -98,6 +98,22 @@ SocketPosts.getPostSummaryByIndex = async function (socket, data) {
|
||||
return postsData[0];
|
||||
};
|
||||
|
||||
SocketPosts.getPostSummaryByPid = async function (socket, data) {
|
||||
if (!data || !data.pid) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const { pid } = data;
|
||||
const tid = await posts.getPostField(pid, 'tid');
|
||||
const topicPrivileges = await privileges.topics.get(tid, socket.uid);
|
||||
if (!topicPrivileges['topics:read']) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
const postsData = await posts.getPostSummaryByPids([pid], socket.uid, { stripTags: false });
|
||||
posts.modifyPostByPrivilege(postsData[0], topicPrivileges);
|
||||
return postsData[0];
|
||||
};
|
||||
|
||||
SocketPosts.getPost = async function (socket, pid) {
|
||||
sockets.warnDeprecated(socket, 'GET /api/v3/posts/:pid');
|
||||
return await api.posts.get(socket, { pid });
|
||||
|
||||
@@ -193,6 +193,12 @@
|
||||
<option value="first">[[admin/settings/post:teaser.first]]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" data-field="showPostPreviewsOnHover">
|
||||
<span class="mdl-switch__label"><strong>[[admin/settings/post:showPostPreviewsOnHover]]</strong></span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
13
src/views/partials/topic/post-preview.tpl
Normal file
13
src/views/partials/topic/post-preview.tpl
Normal file
@@ -0,0 +1,13 @@
|
||||
<div id="post-tooltip" class="well" style="position:absolute; z-index: 1;">
|
||||
<div class="clearfix">
|
||||
<div class="icon pull-left">
|
||||
<a href="{{{ if post.user.userslug }}}{config.relative_path}/user/{post.user.userslug}{{{ else }}}#{{{ end }}}">
|
||||
{buildAvatar(post.user, "sm", true, "", "user/picture")} {post.user.username}
|
||||
</a>
|
||||
</div>
|
||||
<small class="pull-right">
|
||||
<span class="timeago" title="{post.timestampISO}"></span>
|
||||
</small>
|
||||
</div>
|
||||
<div class="content">{post.content}</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user