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
+ }
+ 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
that contains a lot of hashtags, add a class to it - if (enhancedContent.includes('#')) { + // Get the
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
- } 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;
}
}