Perf fixes

This commit is contained in:
Lim Chee Aun 2023-06-11 23:28:12 +08:00
parent 69703df4e1
commit cab2e47a77
3 changed files with 162 additions and 136 deletions

View file

@ -1819,7 +1819,7 @@ const unfurlMastodonLink = throttle(
const root = document.documentElement; const root = document.documentElement;
const defaultBoundingBoxPadding = 8; const defaultBoundingBoxPadding = 8;
function safeBoundingBoxPadding() { function _safeBoundingBoxPadding() {
// Get safe area inset variables from root // Get safe area inset variables from root
const style = getComputedStyle(root); const style = getComputedStyle(root);
const safeAreaInsetTop = style.getPropertyValue('--sai-top'); const safeAreaInsetTop = style.getPropertyValue('--sai-top');
@ -1837,6 +1837,9 @@ function safeBoundingBoxPadding() {
// console.log(str); // console.log(str);
return str; return str;
} }
const safeBoundingBoxPadding = mem(_safeBoundingBoxPadding, {
maxAge: 10_000, // 10 seconds
});
function FilteredStatus({ status, filterInfo, instance, containerProps = {} }) { function FilteredStatus({ status, filterInfo, instance, containerProps = {} }) {
const { const {

View file

@ -7,174 +7,193 @@ function enhanceContent(content, opts = {}) {
let enhancedContent = content; let enhancedContent = content;
const dom = document.createElement('div'); const dom = document.createElement('div');
dom.innerHTML = enhancedContent; dom.innerHTML = enhancedContent;
const hasLink = /<a/i.test(enhancedContent);
const hasCodeBlock = enhancedContent.indexOf('```') !== -1;
// Add target="_blank" to all links with no target="_blank" // Add target="_blank" to all links with no target="_blank"
// E.g. `note` in `account` // E.g. `note` in `account`
const noTargetBlankLinks = Array.from( if (hasLink) {
dom.querySelectorAll('a:not([target="_blank"])'), const noTargetBlankLinks = Array.from(
); dom.querySelectorAll('a:not([target="_blank"])'),
noTargetBlankLinks.forEach((link) => { );
link.setAttribute('target', '_blank'); noTargetBlankLinks.forEach((link) => {
}); link.setAttribute('target', '_blank');
});
}
// Spanify un-spanned mentions // Spanify un-spanned mentions
const notMentionLinks = Array.from(dom.querySelectorAll('a[href]')); if (hasLink) {
notMentionLinks.forEach((link) => { const notMentionLinks = Array.from(dom.querySelectorAll('a[href]'));
const text = link.innerText.trim(); notMentionLinks.forEach((link) => {
const hasChildren = link.querySelector('*'); const text = link.innerText.trim();
// If text looks like @username@domain, then it's a mention const hasChildren = link.querySelector('*');
if (/^@[^@]+(@[^@]+)?$/g.test(text)) { // If text looks like @username@domain, then it's a mention
// Only show @username if (/^@[^@]+(@[^@]+)?$/g.test(text)) {
const username = text.split('@')[1]; // Only show @username
if (!hasChildren) link.innerHTML = `@<span>${username}</span>`; const username = text.split('@')[1];
link.classList.add('mention'); if (!hasChildren) link.innerHTML = `@<span>${username}</span>`;
} link.classList.add('mention');
// If text looks like #hashtag, then it's a hashtag }
if (/^#[^#]+$/g.test(text)) { // If text looks like #hashtag, then it's a hashtag
if (!hasChildren) link.innerHTML = `#<span>${text.slice(1)}</span>`; if (/^#[^#]+$/g.test(text)) {
link.classList.add('mention', 'hashtag'); if (!hasChildren) link.innerHTML = `#<span>${text.slice(1)}</span>`;
} link.classList.add('mention', 'hashtag');
}); }
});
}
// EMOJIS // EMOJIS
// ====== // ======
// Convert :shortcode: to <img /> // Convert :shortcode: to <img />
let textNodes = extractTextNodes(dom); let textNodes;
textNodes.forEach((node) => { if (enhancedContent.indexOf(':') !== -1) {
let html = node.nodeValue textNodes = extractTextNodes(dom);
.replace(/&/g, '&amp;') textNodes.forEach((node) => {
.replace(/</g, '&lt;') let html = node.nodeValue
.replace(/>/g, '&gt;'); .replace(/&/g, '&amp;')
if (emojis) { .replace(/</g, '&lt;')
html = emojifyText(html, emojis); .replace(/>/g, '&gt;');
} if (emojis) {
fauxDiv.innerHTML = html; html = emojifyText(html, emojis);
const nodes = Array.from(fauxDiv.childNodes); }
node.replaceWith(...nodes); fauxDiv.innerHTML = html;
}); const nodes = Array.from(fauxDiv.childNodes);
node.replaceWith(...nodes);
});
}
// CODE BLOCKS // CODE BLOCKS
// =========== // ===========
// Convert ```code``` to <pre><code>code</code></pre> // Convert ```code``` to <pre><code>code</code></pre>
const blocks = Array.from(dom.querySelectorAll('p')).filter((p) => if (hasCodeBlock) {
/^```[^]+```$/g.test(p.innerText.trim()), const blocks = Array.from(dom.querySelectorAll('p')).filter((p) =>
); /^```[^]+```$/g.test(p.innerText.trim()),
blocks.forEach((block) => { );
const pre = document.createElement('pre'); blocks.forEach((block) => {
// Replace <br /> with newlines const pre = document.createElement('pre');
block.querySelectorAll('br').forEach((br) => br.replaceWith('\n')); // Replace <br /> with newlines
pre.innerHTML = `<code>${block.innerHTML.trim()}</code>`; block.querySelectorAll('br').forEach((br) => br.replaceWith('\n'));
block.replaceWith(pre); pre.innerHTML = `<code>${block.innerHTML.trim()}</code>`;
}); block.replaceWith(pre);
});
}
// Convert multi-paragraph code blocks to <pre><code>code</code></pre> // Convert multi-paragraph code blocks to <pre><code>code</code></pre>
const paragraphs = Array.from(dom.querySelectorAll('p')); if (hasCodeBlock) {
// Filter out paragraphs with ``` in beginning only const paragraphs = Array.from(dom.querySelectorAll('p'));
const codeBlocks = paragraphs.filter((p) => /^```/g.test(p.innerText)); // Filter out paragraphs with ``` in beginning only
// For each codeBlocks, get all paragraphs until the last paragraph with ``` at the end only const codeBlocks = paragraphs.filter((p) => /^```/g.test(p.innerText));
codeBlocks.forEach((block) => { // For each codeBlocks, get all paragraphs until the last paragraph with ``` at the end only
const nextParagraphs = [block]; codeBlocks.forEach((block) => {
let hasCodeBlock = false; const nextParagraphs = [block];
let currentBlock = block; let hasCodeBlock = false;
while (currentBlock.nextElementSibling) { let currentBlock = block;
const next = currentBlock.nextElementSibling; while (currentBlock.nextElementSibling) {
if (next && next.tagName === 'P') { const next = currentBlock.nextElementSibling;
if (/```$/g.test(next.innerText)) { if (next && next.tagName === 'P') {
nextParagraphs.push(next); if (/```$/g.test(next.innerText)) {
hasCodeBlock = true; nextParagraphs.push(next);
break; hasCodeBlock = true;
break;
} else {
nextParagraphs.push(next);
}
} else { } else {
nextParagraphs.push(next); break;
} }
} else { currentBlock = next;
break;
} }
currentBlock = next; if (hasCodeBlock) {
} const pre = document.createElement('pre');
if (hasCodeBlock) { nextParagraphs.forEach((p) => {
const pre = document.createElement('pre'); // Replace <br /> with newlines
nextParagraphs.forEach((p) => { p.querySelectorAll('br').forEach((br) => br.replaceWith('\n'));
// Replace <br /> with newlines });
p.querySelectorAll('br').forEach((br) => br.replaceWith('\n')); const codeText = nextParagraphs.map((p) => p.innerHTML).join('\n\n');
}); pre.innerHTML = `<code>${codeText}</code>`;
const codeText = nextParagraphs.map((p) => p.innerHTML).join('\n\n'); block.replaceWith(pre);
pre.innerHTML = `<code>${codeText}</code>`; nextParagraphs.forEach((p) => p.remove());
block.replaceWith(pre); }
nextParagraphs.forEach((p) => p.remove()); });
} }
});
// INLINE CODE // INLINE CODE
// =========== // ===========
// Convert `code` to <code>code</code> // Convert `code` to <code>code</code>
textNodes = extractTextNodes(dom); if (enhancedContent.indexOf('`') !== -1) {
textNodes.forEach((node) => { textNodes = extractTextNodes(dom);
let html = node.nodeValue textNodes.forEach((node) => {
.replace(/&/g, '&amp;') let html = node.nodeValue
.replace(/</g, '&lt;') .replace(/&/g, '&amp;')
.replace(/>/g, '&gt;'); .replace(/</g, '&lt;')
if (/`[^`]+`/g.test(html)) { .replace(/>/g, '&gt;');
html = html.replaceAll(/(`[^]+?`)/g, '<code>$1</code>'); if (/`[^`]+`/g.test(html)) {
} html = html.replaceAll(/(`[^]+?`)/g, '<code>$1</code>');
fauxDiv.innerHTML = html; }
const nodes = Array.from(fauxDiv.childNodes); fauxDiv.innerHTML = html;
node.replaceWith(...nodes); const nodes = Array.from(fauxDiv.childNodes);
}); node.replaceWith(...nodes);
});
}
// TWITTER USERNAMES // TWITTER USERNAMES
// ================= // =================
// Convert @username@twitter.com to <a href="https://twitter.com/username">@username@twitter.com</a> // Convert @username@twitter.com to <a href="https://twitter.com/username">@username@twitter.com</a>
textNodes = extractTextNodes(dom, { if (/twitter\.com/i.test(enhancedContent)) {
rejectFilter: ['A'], textNodes = extractTextNodes(dom, {
}); rejectFilter: ['A'],
textNodes.forEach((node) => { });
let html = node.nodeValue textNodes.forEach((node) => {
.replace(/&/g, '&amp;') let html = node.nodeValue
.replace(/</g, '&lt;') .replace(/&/g, '&amp;')
.replace(/>/g, '&gt;'); .replace(/</g, '&lt;')
if (/@[a-zA-Z0-9_]+@twitter\.com/g.test(html)) { .replace(/>/g, '&gt;');
html = html.replaceAll( if (/@[a-zA-Z0-9_]+@twitter\.com/g.test(html)) {
/(@([a-zA-Z0-9_]+)@twitter\.com)/g, html = html.replaceAll(
'<a href="https://twitter.com/$2" rel="nofollow noopener noreferrer" target="_blank">$1</a>', /(@([a-zA-Z0-9_]+)@twitter\.com)/g,
); '<a href="https://twitter.com/$2" rel="nofollow noopener noreferrer" target="_blank">$1</a>',
} );
fauxDiv.innerHTML = html; }
const nodes = Array.from(fauxDiv.childNodes); fauxDiv.innerHTML = html;
node.replaceWith(...nodes); const nodes = Array.from(fauxDiv.childNodes);
}); node.replaceWith(...nodes);
});
}
// HASHTAG STUFFING // HASHTAG STUFFING
// ================ // ================
// Get the <p> that contains a lot of hashtags, add a class to it // Get the <p> that contains a lot of hashtags, add a class to it
const hashtagStuffedParagraph = Array.from(dom.querySelectorAll('p')).find( if (enhancedContent.indexOf('#') !== -1) {
(p) => { const hashtagStuffedParagraph = Array.from(dom.querySelectorAll('p')).find(
let hashtagCount = 0; (p) => {
for (let i = 0; i < p.childNodes.length; i++) { let hashtagCount = 0;
const node = p.childNodes[i]; for (let i = 0; i < p.childNodes.length; i++) {
const node = p.childNodes[i];
if (node.nodeType === Node.TEXT_NODE) { if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent.trim(); const text = node.textContent.trim();
if (text !== '') { if (text !== '') {
return false; return false;
} }
} else if (node.tagName === 'A') { } else if (node.tagName === 'A') {
const linkText = node.textContent.trim(); const linkText = node.textContent.trim();
if (!linkText || !linkText.startsWith('#')) { if (!linkText || !linkText.startsWith('#')) {
return false; return false;
} else {
hashtagCount++;
}
} else { } else {
hashtagCount++; return false;
} }
} else {
return false;
} }
} // Only consider "stuffing" if there are more than 3 hashtags
// Only consider "stuffing" if there are more than 3 hashtags return hashtagCount > 3;
return hashtagCount > 3; },
}, );
); if (hashtagStuffedParagraph) {
if (hashtagStuffedParagraph) { hashtagStuffedParagraph.classList.add('hashtag-stuffing');
hashtagStuffedParagraph.classList.add('hashtag-stuffing'); hashtagStuffedParagraph.title = hashtagStuffedParagraph.innerText;
hashtagStuffedParagraph.title = hashtagStuffedParagraph.innerText; }
} }
if (postEnhanceDOM) { if (postEnhanceDOM) {

View file

@ -1,6 +1,7 @@
import { match } from '@formatjs/intl-localematcher'; import { match } from '@formatjs/intl-localematcher';
import mem from 'mem';
function localeMatch(...args) { function _localeMatch(...args) {
// Wrap in try/catch because localeMatcher throws on invalid locales // Wrap in try/catch because localeMatcher throws on invalid locales
try { try {
return match(...args); return match(...args);
@ -8,5 +9,8 @@ function localeMatch(...args) {
return false; return false;
} }
} }
const localeMatch = mem(_localeMatch, {
cacheKey: (args) => args.join(),
});
export default localeMatch; export default localeMatch;