From 321b60649fdc1137e5e51d4e60a9ca885a26582d Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi <me@steffo.eu> Date: Fri, 4 Oct 2024 22:54:24 +0200 Subject: [PATCH] Make polymaths.social threads actually readable lol --- src/components/status.css | 30 +++++------ src/utils/enhance-content.js | 96 ++++++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/src/components/status.css b/src/components/status.css index 16ce1273..cb283069 100644 --- a/src/components/status.css +++ b/src/components/status.css @@ -1658,29 +1658,31 @@ body:has(#modal-container .carousel) .status .media img:hover { } } -.status:not(.large) .hashtag-stuffing { - opacity: 0.75; +.status .entity-stuffing { + opacity: 0.50; transition: opacity 0.2s ease-in-out; } -.status:not(.large) .hashtag-stuffing:is(:hover, :focus, :focus-within) { +.status .entity-stuffing:is(:hover, :focus, :focus-within) { opacity: 1; } -.status:not(.large) .hashtag-stuffing { +.status:not(.large) .entity-stuffing { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; + + font-size: smaller; /* Convert breaks to spaces */ br { display: none; + * { - margin-inline-start: 1ex; + margin-inline-start: 0.5ex; } } } -.status:not(.large) .hashtag-stuffing:first-child { +.status:not(.large) .entity-stuffing:first-child { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; @@ -1690,13 +1692,13 @@ body:has(#modal-container .carousel) .status .media img:hover { /* If >= 9 hashtags, collapse */ /* TODO: lower the threshold one day */ .status:not(.large, .contextual .status) - p:not(.hashtag-stuffing):has(.hashtag:nth-of-type(1)):has( - .hashtag:nth-of-type(2) - ):has(.hashtag:nth-of-type(3)):has(.hashtag:nth-of-type(4)):has( - .hashtag:nth-of-type(5) - ):has(.hashtag:nth-of-type(6)):has(.hashtag:nth-of-type(7)):has( - .hashtag:nth-of-type(8) - ):has(.hashtag:nth-of-type(9)) { + p:not(.entity-stuffing):has(.mention:nth-of-type(1)):has( + .mention:nth-of-type(2) + ):has(.mention:nth-of-type(3)):has(.mention:nth-of-type(4)):has( + .mention:nth-of-type(5) + ):has(.mention:nth-of-type(6)):has(.mention:nth-of-type(7)):has( + .mention:nth-of-type(8) + ):has(.mention:nth-of-type(9)) { overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; @@ -2561,7 +2563,7 @@ a.card:is(:hover, :focus):visited { display: revert; } - .hashtag-stuffing { + .entity-stuffing { white-space: normal; opacity: 1; } diff --git a/src/utils/enhance-content.js b/src/utils/enhance-content.js index 6796341d..0756d630 100644 --- a/src/utils/enhance-content.js +++ b/src/utils/enhance-content.js @@ -35,6 +35,51 @@ function createDOM(html, isDocumentFragment) { return isDocumentFragment ? tpl.content : tpl; } +function _countEntities(p) { + let count = 0; + + for (const node of p.childNodes) { + if(node.nodeType === Node.TEXT_NODE) { + // Check if there's text between the entities + const text = node.textContent.trim(); + if(text !== '') { + // End if there's text + throw false; + } + } + else if(node.tagName === 'BR') { + // Ignore <br /> + } + else if(node.tagName === 'A') { + // Check if the link has text + const linkText = node.textContent.trim(); + + if(!linkText) { + // End if there's a link without text + throw false; + } + else if(!( + linkText.startsWith('#') || linkText.startsWith('@') + )) { + // End if there's a link that's not a mention or an hashtag + throw false; + } + else { + // This is an entity + count++; + } + } else if(node.tagName === 'SPAN') { + // If this is a span, we might need to go deeper + count += _countEntities(node) + } else { + // There's something else here we should not touch + throw false; + } + } + + return count +} + function _enhanceContent(content, opts = {}) { const { emojis, returnDOM, postEnhanceDOM = () => {} } = opts; let enhancedContent = content; @@ -222,51 +267,40 @@ function _enhanceContent(content, opts = {}) { } } - // HASHTAG STUFFING + // ENTITY STUFFING // ================ - // Get the <p> that contains a lot of hashtags, add a class to it - if (enhancedContent.includes('#')) { + // Get the <p> that contains a lot of hashtags or mentions, add a class to it + if (enhancedContent.includes('#') || enhancedContent.includes('@')) { let prevIndex = null; - const hashtagStuffedParagraphs = [...dom.querySelectorAll('p')].filter( + const stuffedParagraphs = [...dom.querySelectorAll('p')].filter( (p, index) => { - let hashtagCount = 0; - for (let i = 0; i < p.childNodes.length; i++) { - const node = p.childNodes[i]; - - if (node.nodeType === Node.TEXT_NODE) { - const text = node.textContent.trim(); - if (text !== '') { - return false; - } - } else if (node.tagName === 'BR') { - // Ignore <br /> - } else if (node.tagName === 'A') { - const linkText = node.textContent.trim(); - if (!linkText || !linkText.startsWith('#')) { - return false; - } else { - hashtagCount++; - } - } else { - return false; + let entitiesCount = 0; + + try { + entitiesCount = _countEntities(p) + } catch(e) { + if(e === false) { + return false } + throw e; } + // Only consider "stuffing" if: - // - there are more than 3 hashtags - // - there are more than 1 hashtag in adjacent paragraphs - if (hashtagCount > 3) { + // - there are more than 3 entities + // - there are more than 1 entity in adjacent paragraphs + if (entitiesCount > 3) { prevIndex = index; return true; } - if (hashtagCount > 1 && prevIndex && index === prevIndex + 1) { + if (entitiesCount > 1 && prevIndex && index === prevIndex + 1) { prevIndex = index; return true; } }, ); - if (hashtagStuffedParagraphs?.length) { - for (const p of hashtagStuffedParagraphs) { - p.classList.add('hashtag-stuffing'); + if (stuffedParagraphs?.length) { + for (const p of stuffedParagraphs) { + p.classList.add('entity-stuffing'); p.title = p.innerText; } }