From ccd79e53483932a5409d52cf3a2762662c5510ac Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Sun, 20 Aug 2023 10:17:56 +0800
Subject: [PATCH] Further polish hashtag stuffing logic

---
 README.md                    |  6 ++--
 src/utils/enhance-content.js | 64 ++++++++++++++++++++++--------------
 2 files changed, 43 insertions(+), 27 deletions(-)

diff --git a/README.md b/README.md
index 66f2a1ad..d2dcf919 100644
--- a/README.md
+++ b/README.md
@@ -78,9 +78,11 @@ Everything is designed and engineered following my taste and vision. This is a p
 
 ![Hashtag stuffing collapsing](readme-assets/hashtag-stuffing-collapsing.jpg)
 
-- Any paragraphs, except the first one, with more than 3 hashtags will be collapsed.
+- First paragraph of post content with more than 3 hashtags will be collapsed to max 3 lines.
+- Subsequent paragraphs after first paragraph with more than 3 hashtags will be collapsed to 1 line.
+- Adjacent paragraphs with more than 1 hashtag after collapsed paragraphs will be collapsed to 1 line.
 - If there are text around or between the hashtags, they will not be collapsed.
-- Collapsed hashtags will be a single line with `...` at the end.
+- Collapsed hashtags will be appended with `...` at the end.
 - They are also slightly faded out to reduce visual noise.
 - Opening the post view will reveal the hashtags uncollapsed.
 
diff --git a/src/utils/enhance-content.js b/src/utils/enhance-content.js
index 2d2586d6..1a6d70ce 100644
--- a/src/utils/enhance-content.js
+++ b/src/utils/enhance-content.js
@@ -174,35 +174,49 @@ function enhanceContent(content, opts = {}) {
   // ================
   // Get the <p> that contains a lot of hashtags, add a class to it
   if (enhancedContent.indexOf('#') !== -1) {
-    const hashtagStuffedParagraph = Array.from(dom.querySelectorAll('p')).find(
-      (p) => {
-        let hashtagCount = 0;
-        for (let i = 0; i < p.childNodes.length; i++) {
-          const node = p.childNodes[i];
+    let prevIndex = null;
+    const hashtagStuffedParagraphs = Array.from(
+      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 === 'A') {
-            const linkText = node.textContent.trim();
-            if (!linkText || !linkText.startsWith('#')) {
-              return false;
-            } else {
-              hashtagCount++;
-            }
-          } else {
+        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;
         }
-        // Only consider "stuffing" if there are more than 3 hashtags
-        return hashtagCount > 3;
-      },
-    );
-    if (hashtagStuffedParagraph) {
-      hashtagStuffedParagraph.classList.add('hashtag-stuffing');
-      hashtagStuffedParagraph.title = hashtagStuffedParagraph.innerText;
+      }
+      // Only consider "stuffing" if:
+      // - there are more than 3 hashtags
+      // - there are more than 1 hashtag in adjacent paragraphs
+      if (hashtagCount > 3) {
+        prevIndex = index;
+        return true;
+      }
+      if (hashtagCount > 1 && prevIndex && index === prevIndex + 1) {
+        prevIndex = index;
+        return true;
+      }
+    });
+    if (hashtagStuffedParagraphs?.length) {
+      hashtagStuffedParagraphs.forEach((p) => {
+        p.classList.add('hashtag-stuffing');
+        p.title = p.innerText;
+      });
     }
   }