Merge pull request #2392 from PR0M3TH3AN/perf/lazy-loading-images-5531542145161108085

⚡ Optimize image loading with lazy and async attributes
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-12 12:06:35 -05:00
commit b958cd03a3
6 files changed
+62

No files matched your search

+2
View File
@@ -1241,6 +1241,8 @@ export function buildHistoryCard({ item, video, profile, variant }) {
: FALLBACK_AVATAR;
avatarImg.src = avatarSrc;
avatarImg.alt = profile?.name || "Avatar";
avatarImg.loading = "lazy";
avatarImg.decoding = "async";
avatarBtn.appendChild(avatarImg);
const authorNameBtn = document.createElement("button");
+1
View File
@@ -127,6 +127,7 @@ function renderProfileCards(profiles, container) {
img.alt = name;
img.className = "w-full h-full object-cover";
img.loading = "lazy";
img.decoding = "async";
img.addEventListener("error", (e) => {
e.target.src = FALLBACK_AVATAR;
});
@@ -390,6 +390,8 @@ export class ProfileDirectMessageRenderer {
img.src = result.objectUrl;
img.alt = attachment.name || "Attachment preview";
img.className = "h-40 w-full rounded-lg object-cover";
img.loading = "lazy";
img.decoding = "async";
attachmentCard.appendChild(img);
} else if (attachment.type?.startsWith("video/")) {
const video = document.createElement("video");
@@ -884,6 +886,8 @@ export class ProfileDirectMessageRenderer {
avatarImg.className = "h-full w-full object-cover";
avatarImg.src = avatarUrl;
avatarImg.alt = `${safeName} avatar`;
avatarImg.loading = "lazy";
avatarImg.decoding = "async";
avatarWrapper.appendChild(avatarImg);
const textStack = document.createElement("div");
+4
View File
@@ -1471,6 +1471,8 @@ export class ProfileModalController {
img.src = entry.previewUrl;
img.alt = entry.name || "Attachment preview";
img.className = "h-24 w-24 rounded-lg object-cover";
img.loading = "lazy";
img.decoding = "async";
item.appendChild(img);
}
@@ -2892,6 +2894,8 @@ export class ProfileModalController {
? formatNpub(meta.npub) || DEFAULT_SAVED_PROFILE_LABEL
: DEFAULT_SAVED_PROFILE_LABEL);
avatarImg.alt = `${cardDisplayName} avatar`;
avatarImg.loading = "lazy";
avatarImg.decoding = "async";
avatarSpan.appendChild(avatarImg);
const metaSpan = document.createElement("div");
+2
View File
@@ -255,6 +255,8 @@ export class SubscriptionHistoryController {
const img = document.createElement("img");
img.src = avatar;
img.loading = "lazy";
img.decoding = "async";
img.className = "w-8 h-8 rounded-full object-cover bg-surface-2";
img.onerror = function () {
this.src = "assets/svg/default-profile.svg";
+49
View File
@@ -0,0 +1,49 @@
import fs from 'fs';
import path from 'path';
const filesToCheck = [
'js/historyView.js',
'js/ui/subscriptionHistoryController.js',
'js/searchView.js',
'js/ui/profileModal/ProfileDirectMessageRenderer.js',
'js/ui/profileModalController.js'
];
let totalImages = 0;
let missingLazy = 0;
let missingAsync = 0;
filesToCheck.forEach(file => {
const content = fs.readFileSync(file, 'utf8');
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.includes('createElement("img")') || line.includes("createElement('img')")) {
totalImages++;
// simple heuristic: look ahead next 20 lines for attributes
let chunk = lines.slice(i, i + 20).join('\n');
// Check for variable name
const match = line.match(/(?:const|let|var)\s+(\w+)\s*=/);
const varName = match ? match[1] : null;
if (varName) {
if (!chunk.includes(`${varName}.loading = "lazy"`)) {
missingLazy++;
console.log(`[${file}] Missing loading="lazy" for ${varName} at line ${i+1}`);
}
if (!chunk.includes(`${varName}.decoding = "async"`)) {
missingAsync++;
console.log(`[${file}] Missing decoding="async" for ${varName} at line ${i+1}`);
}
}
}
}
});
console.log('--- Results ---');
console.log(`Total images found: ${totalImages}`);
console.log(`Missing loading="lazy": ${missingLazy}`);
console.log(`Missing decoding="async": ${missingAsync}`);