mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-03-25 06:54:43 +01:00
Perf fixes
This commit is contained in:
parent
69703df4e1
commit
cab2e47a77
3 changed files with 162 additions and 136 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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, '&')
|
textNodes.forEach((node) => {
|
||||||
.replace(/</g, '<')
|
let html = node.nodeValue
|
||||||
.replace(/>/g, '>');
|
.replace(/&/g, '&')
|
||||||
if (emojis) {
|
.replace(/</g, '<')
|
||||||
html = emojifyText(html, emojis);
|
.replace(/>/g, '>');
|
||||||
}
|
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, '&')
|
let html = node.nodeValue
|
||||||
.replace(/</g, '<')
|
.replace(/&/g, '&')
|
||||||
.replace(/>/g, '>');
|
.replace(/</g, '<')
|
||||||
if (/`[^`]+`/g.test(html)) {
|
.replace(/>/g, '>');
|
||||||
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, '&')
|
let html = node.nodeValue
|
||||||
.replace(/</g, '<')
|
.replace(/&/g, '&')
|
||||||
.replace(/>/g, '>');
|
.replace(/</g, '<')
|
||||||
if (/@[a-zA-Z0-9_]+@twitter\.com/g.test(html)) {
|
.replace(/>/g, '>');
|
||||||
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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue