perf: call getIconbackgrounds once

instead of 20x on topic/topic list load
This commit is contained in:
Barış Soner Uşaklı
2024-06-11 17:20:31 -04:00
parent 1c44f55d3c
commit 1dadd16f4b
9 changed files with 41 additions and 38 deletions

View File

@@ -331,6 +331,12 @@ UserObjectFull:
example: example:
- administrators - administrators
- Staff - Staff
iconBackgrounds:
type: array
items:
type: string
description: A valid CSS colour code
example: '#fff'
muted: muted:
type: boolean type: boolean
description: Whether or not the user has been muted. description: Whether or not the user has been muted.

View File

@@ -152,12 +152,6 @@ get:
type: boolean type: boolean
enableQuickReply: enableQuickReply:
type: boolean type: boolean
iconBackgrounds:
type: array
items:
type: string
description: A valid CSS colour code
example: '#fff'
emailPrompt: emailPrompt:
type: number type: number
useragent: useragent:

View File

@@ -152,12 +152,6 @@ get:
type: boolean type: boolean
enableQuickReply: enableQuickReply:
type: boolean type: boolean
iconBackgrounds:
type: array
items:
type: string
description: A valid CSS colour code
example: '#fff'
emailPrompt: emailPrompt:
type: number type: number
useragent: useragent:

View File

@@ -27,7 +27,7 @@ define('accounts/picture', [
icon: { text: ajaxify.data['icon:text'], bgColor: ajaxify.data['icon:bgColor'] }, icon: { text: ajaxify.data['icon:text'], bgColor: ajaxify.data['icon:bgColor'] },
defaultAvatar: ajaxify.data.defaultAvatar, defaultAvatar: ajaxify.data.defaultAvatar,
allowProfileImageUploads: ajaxify.data.allowProfileImageUploads, allowProfileImageUploads: ajaxify.data.allowProfileImageUploads,
iconBackgrounds: config.iconBackgrounds, iconBackgrounds: ajaxify.data.iconBackgrounds,
user: { user: {
uid: ajaxify.data.uid, uid: ajaxify.data.uid,
username: ajaxify.data.username, username: ajaxify.data.username,

View File

@@ -656,7 +656,7 @@ usersAPI.changePicture = async (caller, data) => {
picture = returnData && returnData.picture; picture = returnData && returnData.picture;
} }
const validBackgrounds = await user.getIconBackgrounds(caller.uid); const validBackgrounds = await user.getIconBackgrounds();
if (!validBackgrounds.includes(data.bgColor)) { if (!validBackgrounds.includes(data.bgColor)) {
data.bgColor = validBackgrounds[0]; data.bgColor = validBackgrounds[0];
} }

View File

@@ -32,11 +32,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {})
await parseAboutMe(results.userData); await parseAboutMe(results.userData);
let { userData } = results; let { userData } = results;
const { userSettings } = results; const { userSettings, isAdmin, isGlobalModerator, isModerator, canViewInfo } = results;
const { isAdmin } = results;
const { isGlobalModerator } = results;
const { isModerator } = results;
const { canViewInfo } = results;
const isSelf = parseInt(callerUID, 10) === parseInt(userData.uid, 10); const isSelf = parseInt(callerUID, 10) === parseInt(userData.uid, 10);
if (meta.config['reputation:disabled']) { if (meta.config['reputation:disabled']) {
@@ -84,6 +80,7 @@ helpers.getUserDataByUserSlug = async function (userslug, callerUID, query = {})
userData.isFollowing = results.isFollowing; userData.isFollowing = results.isFollowing;
userData.canChat = results.canChat; userData.canChat = results.canChat;
userData.hasPrivateChat = results.hasPrivateChat; userData.hasPrivateChat = results.hasPrivateChat;
userData.iconBackgrounds = results.iconBackgrounds;
userData.showHidden = results.canEdit; // remove in v1.19.0 userData.showHidden = results.canEdit; // remove in v1.19.0
userData.allowProfilePicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:profile-picture']; userData.allowProfilePicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:profile-picture'];
userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture']; userData.allowCoverPicture = !userData.isSelf || !!meta.config['reputation:disabled'] || userData.reputation >= meta.config['min:rep:cover-picture'];
@@ -160,6 +157,7 @@ async function getAllData(uid, callerUID) {
canViewInfo: privileges.global.can('view:users:info', callerUID), canViewInfo: privileges.global.can('view:users:info', callerUID),
canChat: canChat(callerUID, uid), canChat: canChat(callerUID, uid),
hasPrivateChat: messaging.hasPrivateChat(callerUID, uid), hasPrivateChat: messaging.hasPrivateChat(callerUID, uid),
iconBackgrounds: user.getIconBackgrounds(),
}); });
} }

View File

@@ -88,7 +88,6 @@ apiController.loadConfig = async function (req) {
thumbs: { thumbs: {
size: meta.config.topicThumbSize, size: meta.config.topicThumbSize,
}, },
iconBackgrounds: await user.getIconBackgrounds(req.uid),
emailPrompt: meta.config.emailPrompt, emailPrompt: meta.config.emailPrompt,
useragent: req.useragent, useragent: req.useragent,
fontawesome: { fontawesome: {

View File

@@ -44,6 +44,8 @@ module.exports = function (User) {
'email:confirmed': 0, 'email:confirmed': 0,
}; };
let iconBackgrounds;
User.getUsersFields = async function (uids, fields) { User.getUsersFields = async function (uids, fields) {
if (!Array.isArray(uids) || !uids.length) { if (!Array.isArray(uids) || !uids.length) {
return []; return [];
@@ -187,8 +189,11 @@ module.exports = function (User) {
['showfullname'] ['showfullname']
)); ));
} }
if (!iconBackgrounds) {
iconBackgrounds = await User.getIconBackgrounds();
}
await Promise.all(users.map(async (user) => { users.forEach((user) => {
if (!user) { if (!user) {
return; return;
} }
@@ -204,7 +209,7 @@ module.exports = function (User) {
user.email = validator.escape(user.email ? user.email.toString() : ''); user.email = validator.escape(user.email ? user.email.toString() : '');
} }
if (!parseInt(user.uid, 10)) { if (!user.uid) {
for (const [key, value] of Object.entries(User.guestData)) { for (const [key, value] of Object.entries(User.guestData)) {
user[key] = value; user[key] = value;
} }
@@ -234,15 +239,12 @@ module.exports = function (User) {
} }
// User Icons // User Icons
if (requestedFields.includes('picture') && user.username && parseInt(user.uid, 10) && !meta.config.defaultAvatar) { if (requestedFields.includes('picture') && user.username && user.uid && !meta.config.defaultAvatar) {
const iconBackgrounds = await User.getIconBackgrounds(user.uid); if (!iconBackgrounds.includes(user['icon:bgColor'])) {
let bgColor = await User.getUserField(user.uid, 'icon:bgColor'); const nameAsIndex = Array.from(user.username).reduce((cur, next) => cur + next.charCodeAt(), 0);
if (!iconBackgrounds.includes(bgColor)) { user['icon:bgColor'] = iconBackgrounds[nameAsIndex % iconBackgrounds.length];
bgColor = Array.prototype.reduce.call(user.username, (cur, next) => cur + next.charCodeAt(), 0);
bgColor = iconBackgrounds[bgColor % iconBackgrounds.length];
} }
user['icon:text'] = (user.username[0] || '').toUpperCase(); user['icon:text'] = (user.username[0] || '').toUpperCase();
user['icon:bgColor'] = bgColor;
} }
if (user.hasOwnProperty('joindate')) { if (user.hasOwnProperty('joindate')) {
@@ -253,6 +255,14 @@ module.exports = function (User) {
user.lastonlineISO = utils.toISOString(user.lastonline) || user.joindateISO; user.lastonlineISO = utils.toISOString(user.lastonline) || user.joindateISO;
} }
if (user.hasOwnProperty('mutedUntil')) {
user.muted = user.mutedUntil > Date.now();
}
});
// TODO get rid of single calls
// dont do anything if user is not banned?
await Promise.all(users.map(async (user) => {
if (user.hasOwnProperty('banned') || user.hasOwnProperty('banned:expire')) { if (user.hasOwnProperty('banned') || user.hasOwnProperty('banned:expire')) {
const result = await User.bans.calcExpiredFromUserData(user); const result = await User.bans.calcExpiredFromUserData(user);
user.banned = result.banned; user.banned = result.banned;
@@ -264,10 +274,6 @@ module.exports = function (User) {
user.banned = false; user.banned = false;
} }
} }
if (user.hasOwnProperty('mutedUntil')) {
user.muted = user.mutedUntil > Date.now();
}
})); }));
return await plugins.hooks.fire('filter:users.get', users); return await plugins.hooks.fire('filter:users.get', users);
@@ -313,14 +319,20 @@ module.exports = function (User) {
} }
} }
User.getIconBackgrounds = async (uid = 0) => {
let iconBackgrounds = [ User.getIconBackgrounds = async () => {
if (iconBackgrounds) {
return iconBackgrounds;
}
const _iconBackgrounds = [
'#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3',
'#009688', '#1b5e20', '#33691e', '#827717', '#e65100', '#ff5722', '#009688', '#1b5e20', '#33691e', '#827717', '#e65100', '#ff5722',
'#795548', '#607d8b', '#795548', '#607d8b',
]; ];
({ iconBackgrounds } = await plugins.hooks.fire('filter:user.iconBackgrounds', { uid, iconBackgrounds })); const data = await plugins.hooks.fire('filter:user.iconBackgrounds', { iconBackgrounds: _iconBackgrounds });
iconBackgrounds = data.iconBackgrounds;
return iconBackgrounds; return iconBackgrounds;
}; };

View File

@@ -607,7 +607,7 @@ describe('User', () => {
it('should return an icon text and valid background if username and picture is explicitly requested', async () => { it('should return an icon text and valid background if username and picture is explicitly requested', async () => {
const payload = await User.getUserFields(testUid, ['username', 'picture']); const payload = await User.getUserFields(testUid, ['username', 'picture']);
const validBackgrounds = await User.getIconBackgrounds(testUid); const validBackgrounds = await User.getIconBackgrounds();
assert.strictEqual(payload['icon:text'], userData.username.slice(0, 1).toUpperCase()); assert.strictEqual(payload['icon:text'], userData.username.slice(0, 1).toUpperCase());
assert(payload['icon:bgColor']); assert(payload['icon:bgColor']);
assert(validBackgrounds.includes(payload['icon:bgColor'])); assert(validBackgrounds.includes(payload['icon:bgColor']));
@@ -616,7 +616,7 @@ describe('User', () => {
it('should return a valid background, even if an invalid background colour is set', async () => { it('should return a valid background, even if an invalid background colour is set', async () => {
await User.setUserField(testUid, 'icon:bgColor', 'teal'); await User.setUserField(testUid, 'icon:bgColor', 'teal');
const payload = await User.getUserFields(testUid, ['username', 'picture']); const payload = await User.getUserFields(testUid, ['username', 'picture']);
const validBackgrounds = await User.getIconBackgrounds(testUid); const validBackgrounds = await User.getIconBackgrounds();
assert(payload['icon:bgColor']); assert(payload['icon:bgColor']);
assert(validBackgrounds.includes(payload['icon:bgColor'])); assert(validBackgrounds.includes(payload['icon:bgColor']));