refactor: add placeholders on demand

remove emptylines/whitespace from tpls
This commit is contained in:
Barış Soner Uşaklı
2024-06-16 22:56:21 -04:00
parent 461e95d8d6
commit 8f486b1b99
7 changed files with 61 additions and 32 deletions

View File

@@ -104,10 +104,10 @@
"nodebb-plugin-ntfy": "1.7.4",
"nodebb-plugin-spam-be-gone": "2.2.2",
"nodebb-rewards-essentials": "1.0.0",
"nodebb-theme-harmony": "1.2.57",
"nodebb-theme-harmony": "1.2.58",
"nodebb-theme-lavender": "7.1.8",
"nodebb-theme-peace": "2.2.5",
"nodebb-theme-persona": "13.3.20",
"nodebb-theme-persona": "13.3.21",
"nodebb-widget-essentials": "7.0.16",
"nodemailer": "6.9.13",
"nprogress": "0.2.0",

View File

@@ -36,7 +36,7 @@ define('forum/topic/postTools', [
if (!container) {
return;
}
$('[component="topic"]').on('show.bs.dropdown', '.moderator-tools', function () {
$('[component="topic"]').on('show.bs.dropdown', '[component="post/tools"]', function () {
const $this = $(this);
const dropdownMenu = $this.find('.dropdown-menu');
const { top } = this.getBoundingClientRect();
@@ -45,6 +45,10 @@ define('forum/topic/postTools', [
if (dropdownMenu.attr('data-loaded')) {
return;
}
dropdownMenu.html(helpers.generatePlaceholderWave([
3, 5, 9, 7, 10, 'divider', 10,
]));
const postEl = $this.parents('[data-pid]');
const pid = postEl.attr('data-pid');
const index = parseInt(postEl.attr('data-index'), 10);

View File

@@ -8,21 +8,22 @@ define('forum/topic/replies', ['forum/topic/posts', 'hooks', 'alerts', 'api'], f
const post = button.closest('[data-pid]');
const pid = post.data('pid');
const open = button.find('[component="post/replies/open"]');
const loading = button.find('[component="post/replies/loading"]');
const close = button.find('[component="post/replies/close"]');
if (open.is(':not(.hidden)') && loading.is('.hidden')) {
open.addClass('hidden');
loading.removeClass('hidden');
if (open.attr('loading') !== '1' && open.attr('loaded') !== '1') {
open.attr('loading', '1')
.removeClass('fa-chevron-down')
.addClass('fa-spin fa-spinner');
api.get(`/posts/${pid}/replies`, {}, function (err, { replies }) {
const postData = replies;
loading.addClass('hidden');
open.removeAttr('loading')
.attr('loaded', '1')
.removeClass('fa-spin fa-spinner')
.addClass('fa-chevron-up');
if (err) {
open.removeClass('hidden');
return alerts.error(err);
}
close.removeClass('hidden');
postData.forEach((post, index) => {
if (post) {
post.index = index;
@@ -50,10 +51,11 @@ define('forum/topic/replies', ['forum/topic/posts', 'hooks', 'alerts', 'api'], f
hooks.fire('action:posts.loaded', { posts: postData });
});
});
} else if (close.is(':not(.hidden)')) {
close.addClass('hidden');
open.removeClass('hidden');
loading.addClass('hidden');
} else if (open.attr('loaded') === '1') {
open.removeAttr('loaded')
.removeAttr('loading')
.removeClass('fa-spin fa-spinner fa-chevron-up')
.addClass('fa-chevron-down');
post.find('[component="post/replies"]').slideUp('fast', function () {
$(this).remove();
});

View File

@@ -11,7 +11,8 @@ define('forum/topic/threadTools', [
'bootbox',
'alerts',
'bootstrap',
], function (components, translator, handleBack, posts, api, hooks, bootbox, alerts, bootstrap) {
'helpers',
], function (components, translator, handleBack, posts, api, hooks, bootbox, alerts, bootstrap, helpers) {
const ThreadTools = {};
ThreadTools.init = function (tid, topicContainer) {
@@ -211,6 +212,7 @@ define('forum/topic/threadTools', [
if (dropdownMenu.attr('data-loaded')) {
return;
}
dropdownMenu.html(helpers.generatePlaceholderWave([8, 8, 8]));
const data = await socket.emit('topics.loadTopicTools', { tid: ajaxify.data.tid, cid: ajaxify.data.cid });
const html = await app.parseAndTranslate('partials/topic/topic-menu-list', data);
$(dropdownMenu).attr('data-loaded', 'true').html(html);

View File

@@ -25,12 +25,14 @@ module.exports = function (utils, Benchpress, relative_path) {
userAgentIcons,
buildAvatar,
increment,
generateWroteReplied,
generateRepliedTo,
generateWrote,
isoTimeToLocaleString,
shouldHideReplyContainer,
humanReadableNumber,
formattedNumber,
generatePlaceholderWave,
register,
__escape: identity,
};
@@ -295,34 +297,24 @@ module.exports = function (utils, Benchpress, relative_path) {
if (!userObj) {
userObj = this;
}
classNames = classNames || '';
const attributes = new Map([
['alt', userObj.username],
['title', userObj.username],
['data-uid', userObj.uid],
['loading', 'lazy'],
['aria-label', `[[aria:user-avatar-for, ${userObj.username}]]`],
['class', `avatar ${classNames}${rounded ? ' avatar-rounded' : ''}`],
]);
const styles = [`--avatar-size: ${size};`];
const attr2String = attributes => Array.from(attributes).reduce((output, [prop, value]) => {
output += ` ${prop}="${value}"`;
return output;
}, '');
classNames = classNames || '';
attributes.set('class', `avatar ${classNames}${rounded ? ' avatar-rounded' : ''}`);
let output = '';
if (userObj.picture) {
attributes.set('component', component || 'avatar/picture');
output += '<img ' + attr2String(attributes) + ' src="' + userObj.picture + '" style="' + styles.join(' ') + '" onError="this.remove();" itemprop="image" />';
output += `<img${attr2String(attributes)} alt="${userObj.username}" loading="lazy" component="${component || 'avatar/picture'}" src="${userObj.picture}" style="${styles.join(' ')}" onError="this.remove()" itemprop="image" />`;
}
attributes.set('component', component || 'avatar/icon');
styles.push('background-color: ' + userObj['icon:bgColor'] + ';');
output += '<span ' + attr2String(attributes) + ' style="' + styles.join(' ') + '">' + userObj['icon:text'] + '</span>';
output += `<span${attr2String(attributes)} component="${component || 'avatar/icon'}" style="${styles.join(' ')} background-color: ${userObj['icon:bgColor']}">${userObj['icon:text']}</span>`;
return output;
}
@@ -330,6 +322,13 @@ module.exports = function (utils, Benchpress, relative_path) {
return String(value + parseInt(inc, 10));
}
function generateWroteReplied(post, timeagoCutoff) {
if (post.toPid) {
return generateRepliedTo(post, timeagoCutoff);
}
return generateWrote(post, timeagoCutoff);
}
function generateRepliedTo(post, timeagoCutoff) {
const displayname = post.parent && post.parent.displayname ?
post.parent.displayname : '[[global:guest]]';
@@ -367,6 +366,21 @@ module.exports = function (utils, Benchpress, relative_path) {
return utils.addCommas(number);
}
function generatePlaceholderWave(items) {
const html = items.map((i) => {
if (i === 'divider') {
return '<li class="dropdown-divider"></li>';
}
return `
<li class="dropdown-item placeholder-wave">
<div class="placeholder" style="width: 20px;"></div>
<div class="placeholder col-${i}"></div>
</li>`;
});
return html;
}
function register() {
Object.keys(helpers).forEach(function (helperName) {
Benchpress.registerHelper(helperName, helpers[helperName]);

View File

@@ -89,7 +89,9 @@ apiController.loadConfig = async function (req) {
size: meta.config.topicThumbSize,
},
emailPrompt: meta.config.emailPrompt,
useragent: req.useragent,
useragent: {
isSafari: req.useragent.isSafari,
},
fontawesome: {
pro: fontawesome_pro,
styles: fontawesome_styles,

View File

@@ -114,7 +114,7 @@ async function compile() {
let files = await plugins.getActive();
files = await getTemplateDirs(files);
files = await getTemplateFiles(files);
const minify = process.env.NODE_ENV !== 'development';
await Promise.all(Object.keys(files).map(async (name) => {
const filePath = files[name];
let imported = await fs.promises.readFile(filePath, 'utf8');
@@ -122,6 +122,11 @@ async function compile() {
await mkdirp(path.join(viewsPath, path.dirname(name)));
// remove empty lines and whitespace
if (minify) {
imported = imported.split('\n').map(line => line.trim()).filter(Boolean).join('\n');
}
await fs.promises.writeFile(path.join(viewsPath, name), imported);
const compiled = await Benchpress.precompile(imported, { filename: name });
await fs.promises.writeFile(path.join(viewsPath, name.replace(/\.tpl$/, '.js')), compiled);