From 54314de976ab31d2253cae28e67af20cca64e1c2 Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Fri, 5 Jan 2024 19:15:22 +0800
Subject: [PATCH] Experiment unlinked replies (again)

But still show link to the post's "thread"
---
 src/app.css          |  90 ++++++++++++++++++++++----
 src/index.css        |   1 +
 src/pages/status.jsx | 151 +++++++++++++++++++++++++++----------------
 3 files changed, 176 insertions(+), 66 deletions(-)

diff --git a/src/app.css b/src/app.css
index c311881b..baf91efd 100644
--- a/src/app.css
+++ b/src/app.css
@@ -547,9 +547,10 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
   list-style: none;
 }
 .timeline.contextual > li .replies > .replies-summary {
-  padding: 8px;
+  --summary-padding: 8px;
+  padding: var(--summary-padding);
   background-color: var(--bg-faded-color);
-  display: inline-block;
+  display: inline-flex;
   border-radius: 8px;
   cursor: pointer;
   text-transform: uppercase;
@@ -559,12 +560,67 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
   box-shadow: 0 0 0 2px var(--bg-color);
   position: relative;
   list-style: none;
-  white-space: nowrap;
+  gap: 8px;
+  align-items: center;
+  margin-right: calc(44px + 8px);
 
   b {
     font-weight: 500;
     color: var(--text-color);
   }
+
+  .avatars {
+    flex-shrink: 0;
+    transition: opacity 0.3s ease;
+
+    .avatar {
+      transition: transform 0.3s ease;
+
+      &:not(:first-child) {
+        margin: 0 0 0 -4px;
+      }
+    }
+  }
+
+  .replies-counts {
+    /* flex-grow: 1; */
+
+    > * {
+      display: inline-block;
+    }
+  }
+
+  .replies-summary-chevron {
+    transition: transform 0.3s ease;
+  }
+
+  .replies-parent-link {
+    position: absolute;
+    right: 4px;
+    height: 100%;
+    z-index: 2;
+    font-size: 16px;
+    font-weight: bold;
+    align-self: stretch;
+    text-decoration: none;
+    display: flex;
+    align-items: center;
+    padding: var(--summary-padding) calc(var(--summary-padding) * 2);
+    transform: translateX(100%);
+    margin: calc(-1 * var(--summary-padding)) calc(-1 * var(--summary-padding))
+      calc(-1 * var(--summary-padding)) 0;
+    border-radius: 8px;
+    background-color: var(--link-bg-color);
+
+    &:is(:hover, :focus) {
+      color: var(--link-text-color);
+      box-shadow: inset 0 0 0 2px var(--link-faded-color);
+    }
+
+    &:active {
+      background-color: var(--link-faded-color);
+    }
+  }
 }
 .timeline.contextual > li .replies > .replies-summary::-webkit-details-marker {
   display: none;
@@ -572,14 +628,14 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
 .timeline.contextual > li .replies > .replies-summary > * {
   vertical-align: middle;
 }
-.timeline.contextual > li .replies > .replies-summary .avatars {
-  margin-right: 8px;
-
-  > *:not(:first-child) {
-    margin: 0 0 0 -4px;
-  }
-}
-.timeline.contextual > li .replies > .replies-summary:active,
+.timeline.contextual
+  > li
+  .replies
+  > .replies-summary
+  .timeline.contextual
+  > li
+  .replies
+  > .replies-summary:active,
 .timeline.contextual > li .replies[open] > .replies-summary {
   color: var(--text-color);
   background-color: var(--comment-line-color);
@@ -591,6 +647,18 @@ a[href^='http'][rel*='nofollow']:visited:not(:has(div)) {
 }
 .timeline.contextual > li .replies[open] > .replies-summary {
   border-bottom-left-radius: 0;
+
+  .avatars {
+    opacity: 0.5;
+
+    .avatar {
+      transform: rotate(-15deg);
+    }
+  }
+
+  .replies-summary-chevron {
+    transform: rotate(180deg);
+  }
 }
 .timeline.contextual > li .replies .replies-summary[hidden] {
   display: none;
diff --git a/src/index.css b/src/index.css
index 1cd4d7b2..5da6f219 100644
--- a/src/index.css
+++ b/src/index.css
@@ -32,6 +32,7 @@
   --text-color: #1c1e21;
   --text-insignificant-color: #1c1e2199;
   --link-color: var(--blue-color);
+  --link-bg-color: #4169e122;
   --link-light-color: #4169e199;
   --link-faded-color: #4169e155;
   --link-bg-hover-color: #f0f2f599;
diff --git a/src/pages/status.jsx b/src/pages/status.jsx
index 69612e4f..aa425abb 100644
--- a/src/pages/status.jsx
+++ b/src/pages/status.jsx
@@ -184,6 +184,15 @@ function StatusPage(params) {
   );
 }
 
+function StatusParent(props) {
+  const { linkable, to, onClick, ...restProps } = props;
+  return linkable ? (
+    <Link class="status-link" to={to} onClick={onClick} {...restProps} />
+  ) : (
+    <div class="status-focus" tabIndex={0} {...restProps} />
+  );
+}
+
 function StatusThread({ id, closeLink = '/', instance: propInstance }) {
   const [searchParams, setSearchParams] = useSearchParams();
   const mediaParam = searchParams.get('media');
@@ -705,24 +714,8 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
         weight,
       } = status;
       const isHero = statusID === id;
-      // const StatusParent = useCallback(
-      //   (props) =>
-      //     isThread || thread || ancestor ? (
-      //       <Link
-      //         class="status-link"
-      //         to={
-      //           instance ? `/${instance}/s/${statusID}` : `/s/${statusID}`
-      //         }
-      //         onClick={() => {
-      //           resetScrollPosition(statusID);
-      //         }}
-      //         {...props}
-      //       />
-      //     ) : (
-      //       <div class="status-focus" tabIndex={0} {...props} />
-      //     ),
-      //   [isThread, thread],
-      // );
+      const isLinkable = isThread || ancestor;
+
       return (
         <li
           key={statusID}
@@ -808,25 +801,42 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
               )}
             </>
           ) : (
-            // <StatusParent>
-            <Link
-              class="status-link"
+            <StatusParent
+              linkable={isLinkable}
               to={instance ? `/${instance}/s/${statusID}` : `/s/${statusID}`}
               onClick={() => {
                 resetScrollPosition(statusID);
               }}
             >
-              <InView
-                skip={i !== 0 || !ancestor}
-                threshold={0.5}
-                onChange={(inView) => {
-                  queueMicrotask(() => {
-                    requestAnimationFrame(() => {
-                      setReachTopPost(inView);
+              {/* <Link
+              class="status-link"
+              to={instance ? `/${instance}/s/${statusID}` : `/s/${statusID}`}
+              onClick={() => {
+                resetScrollPosition(statusID);
+              }}
+            > */}
+              {i === 0 && ancestor ? (
+                <InView
+                  threshold={0.5}
+                  onChange={(inView) => {
+                    queueMicrotask(() => {
+                      requestAnimationFrame(() => {
+                        setReachTopPost(inView);
+                      });
                     });
-                  });
-                }}
-              >
+                  }}
+                >
+                  <Status
+                    statusID={statusID}
+                    instance={instance}
+                    withinContext
+                    size={thread || ancestor ? 'm' : 's'}
+                    enableTranslate
+                    onMediaClick={handleMediaClick}
+                    onStatusLinkClick={handleStatusLinkClick}
+                  />
+                </InView>
+              ) : (
                 <Status
                   statusID={statusID}
                   instance={instance}
@@ -836,7 +846,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
                   onMediaClick={handleMediaClick}
                   onStatusLinkClick={handleStatusLinkClick}
                 />
-              </InView>
+              )}
               {ancestor && repliesCount > 1 && (
                 <div class="replies-link">
                   <Icon icon="comment2" />{' '}
@@ -853,8 +863,8 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
                           </span>
                         </div>
                       )} */}
-              {/* </StatusParent> */}
-            </Link>
+            </StatusParent>
+            // </Link>
           )}
           {descendant && replies?.length > 0 && (
             <SubComments
@@ -864,6 +874,10 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
               level={1}
               accWeight={weight}
               openAll={totalDescendants.current < SUBCOMMENTS_OPEN_ALL_LIMIT}
+              parentLink={{
+                to: instance ? `/${instance}/s/${statusID}` : `/s/${statusID}`,
+                onClick: () => resetScrollPosition(statusID),
+              }}
             />
           )}
           {uiState === 'loading' &&
@@ -932,6 +946,11 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
     return ids.map((id) => statusKey(id, instance));
   }, [showMore, statuses, limit, instance]);
 
+  const statusesList = useMemo(
+    () => statuses.slice(0, limit).map(renderStatus),
+    [statuses, limit, renderStatus],
+  );
+
   return (
     <div
       tabIndex="-1"
@@ -1176,7 +1195,7 @@ function StatusThread({ id, closeLink = '/', instance: propInstance }) {
             uiState === 'loading' ? 'loading' : ''
           }`}
         >
-          {statuses.slice(0, limit).map(renderStatus)}
+          {statusesList}
           {showMore > 0 && (
             <li>
               <button
@@ -1244,6 +1263,7 @@ function SubComments({
   level,
   accWeight,
   openAll,
+  parentLink,
 }) {
   const [searchParams, setSearchParams] = useSearchParams();
 
@@ -1330,34 +1350,49 @@ function SubComments({
             />
           ))}
         </span>
-        <b>
-          <span title={replies.length}>{shortenNumber(replies.length)}</span>{' '}
-          repl
-          {replies.length === 1 ? 'y' : 'ies'}
-        </b>
-        {!sameCount && totalComments > 1 && (
-          <>
-            {' '}
-            &middot;{' '}
-            <span>
-              <span title={totalComments}>{shortenNumber(totalComments)}</span>{' '}
-              comment
-              {totalComments === 1 ? '' : 's'}
-            </span>
-          </>
+        <span class="replies-counts">
+          <b>
+            <span title={replies.length}>{shortenNumber(replies.length)}</span>{' '}
+            repl
+            {replies.length === 1 ? 'y' : 'ies'}
+          </b>
+          {!sameCount && totalComments > 1 && (
+            <>
+              {' '}
+              &middot;{' '}
+              <span>
+                <span title={totalComments}>
+                  {shortenNumber(totalComments)}
+                </span>{' '}
+                comment
+                {totalComments === 1 ? '' : 's'}
+              </span>
+            </>
+          )}
+        </span>
+        <Icon icon="chevron-down" class="replies-summary-chevron" />
+        {!!parentLink && (
+          <Link
+            class="replies-parent-link"
+            to={parentLink.to}
+            onClick={parentLink.onClick}
+            title="View post with its replies"
+          >
+            &raquo;
+          </Link>
         )}
       </summary>
       <ul>
         {replies.map((r) => (
           <li key={r.id}>
-            <Link
+            {/* <Link
               class="status-link"
               to={instance ? `/${instance}/s/${r.id}` : `/s/${r.id}`}
               onClick={() => {
                 resetScrollPosition(r.id);
               }}
-            >
-              {/* <div class="status-focus" tabIndex={0}> */}
+            > */}
+            <div class="status-focus" tabIndex={0}>
               <Status
                 statusID={r.id}
                 instance={instance}
@@ -1374,8 +1409,8 @@ function SubComments({
                   </span>
                 </div>
               )}
-              {/* </div> */}
-            </Link>
+            </div>
+            {/* </Link> */}
             {r.replies?.length && (
               <SubComments
                 instance={instance}
@@ -1383,6 +1418,12 @@ function SubComments({
                 level={level + 1}
                 accWeight={!open ? r.weight : totalWeight}
                 openAll={openAll}
+                parentLink={{
+                  to: instance ? `/${instance}/s/${r.id}` : `/s/${r.id}`,
+                  onClick: () => {
+                    resetScrollPosition(r.id);
+                  },
+                }}
               />
             )}
           </li>