Server-side rendering of topic events (#9733)

* style: reformat list of helpers exported

* refactor: move topic events partial into a helper, invoke helper in topic.tpl (see persona), update how events are added to the DOM via addTopicEvents

closes #9731

* style: lint
v1.18.x
Julian Lam 3 years ago committed by GitHub
parent 0743554dd4
commit 152f194aee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -10,7 +10,8 @@ define('forum/topic/posts', [
'components',
'translator',
'hooks',
], function (pagination, infinitescroll, postTools, images, navigator, components, translator, hooks) {
'helpers',
], function (pagination, infinitescroll, postTools, images, navigator, components, translator, hooks, helpers) {
var Posts = { };
Posts.onNewPost = function (data) {
@ -281,56 +282,13 @@ define('forum/topic/posts', [
posts.find('[component="post/content"] img:not(.not-responsive)').addClass('img-responsive');
Posts.addBlockquoteEllipses(posts);
hidePostToolsForDeletedPosts(posts);
addNecroPostMessage(Posts.addTopicEvents);
addNecroPostMessage();
};
Posts.addTopicEvents = function (events) {
events = events || ajaxify.data.events;
if (!events || !Array.isArray(events)) {
return;
}
if (config.topicPostSort !== 'newest_to_oldest' && config.topicPostSort !== 'oldest_to_newest') {
return;
}
// Filter out events already in DOM
const eventIdsInDOM = Array.from(document.querySelectorAll('[component="topic/event"]')).map(el => parseInt(el.getAttribute('data-topic-event-id'), 10));
events = events.filter(event => !eventIdsInDOM.includes(event.id));
let postTimestamps = ajaxify.data.posts.map(post => post.timestamp);
const reverse = config.topicPostSort === 'newest_to_oldest';
events = events.slice(0);
if (reverse) {
events.reverse();
postTimestamps = postTimestamps.slice(1); // OP is always at top, so exclude from calculations
}
Promise.all(events.map((event) => {
const beforeIdx = postTimestamps.findIndex(
timestamp => (reverse ? (timestamp < event.timestamp) : (timestamp > event.timestamp))
);
let postEl;
if (beforeIdx > -1) {
postEl = document.querySelector(`[component="post"][data-pid="${ajaxify.data.posts[beforeIdx + (reverse ? 1 : 0)].pid}"]`);
}
return new Promise((resolve) => {
event.isAdminOrMod = ajaxify.data.privileges.isAdminOrMod;
app.parseAndTranslate('partials/topic/event', event, function (html) {
html = html.get(0);
if (postEl) {
document.querySelector('[component="topic"]').insertBefore(html, postEl);
} else {
document.querySelector('[component="topic"]').append(html);
}
resolve();
});
});
})).then(() => {
const html = helpers.renderEvents.call(ajaxify.data, events);
translator.translate(html, (translated) => {
document.querySelector('[component="topic"]').insertAdjacentHTML('beforeend', translated);
$('[component="topic/event"] .timeago').timeago();
});
};

@ -14,23 +14,25 @@
Benchpress.setGlobal('false', false);
var helpers = {
displayMenuItem: displayMenuItem,
buildMetaTag: buildMetaTag,
buildLinkTag: buildLinkTag,
stringify: stringify,
escape: escape,
stripTags: stripTags,
generateCategoryBackground: generateCategoryBackground,
generateChildrenCategories: generateChildrenCategories,
generateTopicClass: generateTopicClass,
membershipBtn: membershipBtn,
spawnPrivilegeStates: spawnPrivilegeStates,
localeToHTML: localeToHTML,
renderTopicImage: renderTopicImage,
renderDigestAvatar: renderDigestAvatar,
userAgentIcons: userAgentIcons,
buildAvatar: buildAvatar,
register: register,
displayMenuItem,
buildMetaTag,
buildLinkTag,
stringify,
escape,
stripTags,
generateCategoryBackground,
generateChildrenCategories,
generateTopicClass,
membershipBtn,
spawnPrivilegeStates,
localeToHTML,
renderTopicImage,
renderTopicEvents,
renderEvents,
renderDigestAvatar,
userAgentIcons,
buildAvatar,
register,
__escape: identity,
};
@ -208,6 +210,46 @@
return '<img component="user/picture" data-uid="' + topicObj.user.uid + '" src="' + topicObj.user.picture + '" class="user-img" title="' + topicObj.user.username + '" />';
}
function renderTopicEvents(index) {
const start = this.posts[index].timestamp;
const end = this.posts[index + 1] ? this.posts[index + 1].timestamp : Date.now();
const events = this.events.filter(event => event.timestamp >= start && event.timestamp < end);
if (!events.length) {
return '';
}
return renderEvents.call(this, events);
}
function renderEvents(events) {
return events.reduce((html, event) => {
html += `<li component="topic/event" class="timeline-event" data-topic-event-id="${event.id}">
<div class="timeline-badge">
<i class="fa ${event.icon || 'fa-circle'}"></i>
</div>
<span class="timeline-text">
${event.href ? `<a href="${relative_path}${event.href}>${event.text}</a>` : event.text}&nbsp;
</span>
`;
if (event.user) {
if (!event.user.system) {
html += `<span><a href="${relative_path}/user/${event.user.userslug}">${buildAvatar(event.user, 'xs', true)}&nbsp;${event.user.username}</a></span>&nbsp;`;
} else {
html += `<span class="timeline-text">[[global:system-user]]</span>&nbsp;`;
}
}
html += `<span class="timeago timeline-text" title="${event.timestampISO}"></span>`;
if (this.privileges.isAdminOrMod) {
html += `&nbsp;<span component="topic/event/delete" data-topic-event-id="{id}" class="timeline-text pointer" title="[[topic:delete-event]]"><i class="fa fa-trash"></i></span>`;
}
return html;
}, '');
}
function renderDigestAvatar(block) {
if (block.teaser) {
if (block.teaser.user.picture) {

Loading…
Cancel
Save