From 6dd54633e0ba343285addf63e5149e45c0b89c9c Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Sun, 24 Dec 2023 21:07:46 +0800
Subject: [PATCH] Finally revisiting this CW thing

Respect reading:expand:spoilers and reading:expand:media but differently than Mastodon's logic
---
 src/components/status.css | 111 ++++++++++++++++------------------
 src/components/status.jsx | 122 +++++++++++++++++++++++++-------------
 src/index.css             |   2 +-
 src/utils/states.js       |   1 +
 4 files changed, 134 insertions(+), 102 deletions(-)

diff --git a/src/components/status.css b/src/components/status.css
index 69c58480..9b527ccc 100644
--- a/src/components/status.css
+++ b/src/components/status.css
@@ -499,76 +499,69 @@
   padding-bottom: 10px;
 }
 
-.status .content-container.has-spoiler .spoiler {
+.status
+  .content-container.has-spoiler
+  :is(.spoiler-button, .spoiler-media-button):not([hidden]) {
   margin: 4px 0;
   font-size: 90%;
   border: 1px dashed var(--button-bg-color);
   display: flex;
-  gap: 8px;
+  gap: 4px;
   align-items: center;
 }
-.status
-  .content-container.has-spoiler:not(.show-spoiler)
-  .spoiler
-  ~ *:not(.media-container, .card, .media-figure-multiple),
-.status
-  .content-container.has-spoiler:not(.show-spoiler)
-  .spoiler
-  ~ .card
-  .meta-container,
-.status
-  .content-container.has-spoiler:not(.show-spoiler)
-  .spoiler
-  ~ :is(.media-container, .media-figure-multiple)
-  figcaption {
-  filter: blur(5px) invert(0.5);
-  image-rendering: crisp-edges;
-  image-rendering: pixelated;
-  pointer-events: none;
-  user-select: none;
-  contain: layout;
-  transform: scale(0.97);
-  transition: transform 0.1s ease-in-out;
+.status .content-container.has-spoiler:not(.show-spoiler) .spoiler-button {
+  ~ *:not(
+      .media-container,
+      .card,
+      .media-figure-multiple,
+      .spoiler-media-button
+    ),
+  ~ .card .meta-container,
+  ~ :is(.media-container, .media-figure-multiple) figcaption {
+    filter: blur(5px) invert(0.5);
+    image-rendering: crisp-edges;
+    image-rendering: pixelated;
+    pointer-events: none;
+    user-select: none;
+    contain: layout;
+    transform: scale(0.97);
+    transition: transform 0.1s ease-in-out;
+  }
+
+  /* ~ :is(.media-container, .media-figure-multiple) .media > *, */
+  ~ .card > img {
+    filter: blur(32px);
+    image-rendering: crisp-edges;
+    image-rendering: pixelated;
+    animation: none !important;
+  }
+}
+.status .content-container.has-spoiler:not(.show-media) .spoiler-media-button {
+  ~ :is(.media-container, .media-figure-multiple) .media > * {
+    filter: blur(32px);
+    image-rendering: crisp-edges;
+    image-rendering: pixelated;
+    animation: none !important;
+  }
 }
 .status
-  .content-container.has-spoiler:not(.show-spoiler)
-  .spoiler
-  ~ :is(.media-container, .media-figure-multiple)
-  .media
-  > *,
-.status
-  .content-container.has-spoiler:not(.show-spoiler)
-  .spoiler
-  ~ .card
-  > img {
-  filter: blur(32px);
-  image-rendering: crisp-edges;
-  image-rendering: pixelated;
-  animation: none !important;
-}
-.status .content-container.show-spoiler .spoiler {
+  .content-container.show-spoiler
+  :is(.spoiler-button, .spoiler-media-button).spoiling {
   border-style: dotted;
 }
-/* .status
-  .content-container.show-spoiler
-  .spoiler
-  ~ *:not(.media-container, .card),
-.status .content-container.show-spoiler .spoiler ~ .card .meta-container {
-  filter: none !important;
-  transform: none;
-  pointer-events: auto;
-  user-select: auto;
-  text-rendering: auto;
-  image-rendering: auto;
+
+.status .content-container .spoiler-divider {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  color: var(--text-insignificant-color);
+  text-transform: uppercase;
+  font-size: 0.8em;
+  margin-top: 0.25em;
+  margin-bottom: 1em;
+  padding-block: 0.25em;
+  border-bottom: 1px dashed var(--divider-color);
 }
-.status .content-container.show-spoiler .spoiler ~ .media-container .media > *,
-.status .content-container.show-spoiler .spoiler ~ .card > img {
-  filter: none;
-  image-rendering: auto;
-} */
-/* .status .content a:not(.mention):not(:has(span)) {
-  color: inherit;
-} */
 
 .status .content-comment-hint {
   margin-top: 0.25em;
diff --git a/src/components/status.jsx b/src/components/status.jsx
index 1e2bd255..5c76bc89 100644
--- a/src/components/status.jsx
+++ b/src/components/status.jsx
@@ -275,8 +275,21 @@ function Status({
     const prefs = store.account.get('preferences') || {};
     return !!prefs['reading:expand:spoilers'];
   }, []);
+  const readingExpandMedia = useMemo(() => {
+    // default | show_all | hide_all
+    // Ignore hide_all because it means hide *ALL* media including non-sensitive ones
+    const prefs = store.account.get('preferences') || {};
+    return prefs['reading:expand:media'] || 'default';
+  }, []);
+  // FOR TESTING:
+  // const readingExpandSpoilers = true;
+  // const readingExpandMedia = 'show_all';
   const showSpoiler =
-    previewMode || readingExpandSpoilers || !!snapStates.spoilers[id] || false;
+    previewMode || readingExpandSpoilers || !!snapStates.spoilers[id];
+  const showSpoilerMedia =
+    previewMode ||
+    readingExpandMedia === 'show_all' ||
+    !!snapStates.spoilersMedia[id];
 
   if (reblog) {
     // If has statusID, means useItemID (cached in states)
@@ -1078,11 +1091,19 @@ function Status({
     );
     if (activeStatus) {
       const spoilerButton = activeStatus.querySelector(
-        'button.spoiler:not(.spoiling)',
+        '.spoiler-button:not(.spoiling)',
       );
       if (spoilerButton) {
         e.stopPropagation();
         spoilerButton.click();
+      } else {
+        const spoilerMediaButton = activeStatus.querySelector(
+          '.spoiler-media-button:not(.spoiling)',
+        );
+        if (spoilerMediaButton) {
+          e.stopPropagation();
+          spoilerMediaButton.click();
+        }
       }
     }
   });
@@ -1487,7 +1508,9 @@ function Status({
         <div
           class={`content-container ${
             spoilerText || sensitive ? 'has-spoiler' : ''
-          } ${showSpoiler ? 'show-spoiler' : ''}`}
+          } ${showSpoiler ? 'show-spoiler' : ''} ${
+            showSpoilerMedia ? 'show-media' : ''
+          }`}
           data-content-text-weight={contentTextWeight ? textWeight() : null}
           style={
             (isSizeLarge || contentTextWeight) && {
@@ -1508,27 +1531,36 @@ function Status({
                   <EmojiText text={spoilerText} emojis={emojis} />
                 </p>
               </div>
-              <button
-                class={`light spoiler ${showSpoiler ? 'spoiling' : ''}`}
-                type="button"
-                disabled={readingExpandSpoilers}
-                onClick={(e) => {
-                  e.preventDefault();
-                  e.stopPropagation();
-                  if (showSpoiler) {
-                    delete states.spoilers[id];
-                  } else {
-                    states.spoilers[id] = true;
-                  }
-                }}
-              >
-                <Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
-                {readingExpandSpoilers
-                  ? 'Content warning'
-                  : showSpoiler
-                  ? 'Show less'
-                  : 'Show more'}
-              </button>
+              {readingExpandSpoilers || previewMode ? (
+                <div class="spoiler-divider">
+                  <Icon icon="eye-open" /> Content warning
+                </div>
+              ) : (
+                <button
+                  class={`light spoiler-button ${
+                    showSpoiler ? 'spoiling' : ''
+                  }`}
+                  type="button"
+                  onClick={(e) => {
+                    e.preventDefault();
+                    e.stopPropagation();
+                    if (showSpoiler) {
+                      delete states.spoilers[id];
+                      if (!readingExpandSpoilers) {
+                        delete states.spoilersMedia[id];
+                      }
+                    } else {
+                      states.spoilers[id] = true;
+                      if (!readingExpandSpoilers) {
+                        states.spoilersMedia[id] = true;
+                      }
+                    }
+                  }}
+                >
+                  <Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} />{' '}
+                  {showSpoiler ? 'Show less' : 'Show content'}
+                </button>
+              )}
             </>
           )}
           {!!content && (
@@ -1632,24 +1664,30 @@ function Status({
               text={getPostText(status)}
             />
           )}
-          {!spoilerText && sensitive && !!mediaAttachments.length && (
-            <button
-              class={`plain spoiler ${showSpoiler ? 'spoiling' : ''}`}
-              type="button"
-              onClick={(e) => {
-                e.preventDefault();
-                e.stopPropagation();
-                if (showSpoiler) {
-                  delete states.spoilers[id];
-                } else {
-                  states.spoilers[id] = true;
-                }
-              }}
-            >
-              <Icon icon={showSpoiler ? 'eye-open' : 'eye-close'} /> Sensitive
-              content
-            </button>
-          )}
+          {!previewMode &&
+            sensitive &&
+            !!mediaAttachments.length &&
+            readingExpandMedia !== 'show_all' && (
+              <button
+                class={`plain spoiler-media-button ${
+                  showSpoilerMedia ? 'spoiling' : ''
+                }`}
+                type="button"
+                hidden={!readingExpandSpoilers && !!spoilerText}
+                onClick={(e) => {
+                  e.preventDefault();
+                  e.stopPropagation();
+                  if (showSpoilerMedia) {
+                    delete states.spoilersMedia[id];
+                  } else {
+                    states.spoilersMedia[id] = true;
+                  }
+                }}
+              >
+                <Icon icon={showSpoilerMedia ? 'eye-open' : 'eye-close'} />{' '}
+                {showSpoilerMedia ? 'Show less' : 'Show media'}
+              </button>
+            )}
           {!!mediaAttachments.length && (
             <MultipleMediaFigure
               lang={language}
diff --git a/src/index.css b/src/index.css
index 9371df43..09c0d609 100644
--- a/src/index.css
+++ b/src/index.css
@@ -203,7 +203,7 @@ textarea {
   max-width: 100%;
 }
 
-button,
+button:not([hidden]),
 .button {
   display: inline-block;
   padding: 8px 12px;
diff --git a/src/utils/states.js b/src/utils/states.js
index 0a5db2c2..ab422a7d 100644
--- a/src/utils/states.js
+++ b/src/utils/states.js
@@ -29,6 +29,7 @@ const states = proxy({
     counter: 0,
   },
   spoilers: {},
+  spoilersMedia: {},
   scrollPositions: {},
   unfurledLinks: {},
   statusQuotes: {},