From 14807de5520d735c23e88bc484fb545ff3ab2fc3 Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 11 Mar 2022 19:24:18 +0000
Subject: [PATCH] Show displayname in non-narrow thread summeries (#8036)

* Show displayname in non-narrow thread summeries

* Iterate PR

* Iterate PR to use line-height based approach

* Fix hover/focus chevron on thread summary relying on font character
---
 res/css/views/rooms/_EventTile.scss          | 69 ++++++++++++--------
 res/img/compound/chevron-right-12px.svg      | 10 +++
 src/components/views/rooms/ThreadSummary.tsx | 13 +++-
 3 files changed, 64 insertions(+), 28 deletions(-)
 create mode 100644 res/img/compound/chevron-right-12px.svg

diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss
index a63efb7e28..afa05668ec 100644
--- a/res/css/views/rooms/_EventTile.scss
+++ b/res/css/views/rooms/_EventTile.scss
@@ -723,7 +723,7 @@ $left-gutter: 64px;
 
 .mx_ThreadInfo {
     min-width: 267px;
-    max-width: min(calc(100% - 64px), 600px);
+    max-width: min(calc(100% - $left-gutter - 64px), 600px); // leave space on both left & right gutters
     width: fit-content;
     height: 40px;
     position: relative;
@@ -740,41 +740,49 @@ $left-gutter: 64px;
     justify-content: flex-start;
     clear: both;
     overflow: hidden;
+    border: 1px solid $system; // always render a border so the hover effect doesn't require a re-layout
 
-    &:hover {
-        cursor: pointer;
-        border: 1px solid $quinary-content;
-        padding-top: 7px;
-        padding-bottom: 7px;
-        padding-left: 11px;
-        padding-right: 15px;
-    }
-
-    &::after {
-        content: "›";
+    .mx_ThreadInfo_chevron {
         position: absolute;
         top: 0;
         right: 0;
         bottom: 0;
         width: 60px;
-        padding: 0 10px;
-        font-size: 15px;
-        line-height: 39px;
         box-sizing: border-box;
-
-        text-align: right;
-        font-weight: 600;
-
         background: linear-gradient(270deg, $system 52.6%, transparent 100%);
 
         opacity: 0;
-        transform: translateX(20px);
+        transform: translateX(60px);
         transition: all .1s ease-in-out;
+
+        &::before {
+            content: '';
+            position: absolute;
+            top: 50%;
+            right: 12px;
+            transform: translateY(-50%);
+            width: 12px;
+            height: 12px;
+            mask-image: url('$(res)/img/compound/chevron-right-12px.svg');
+            mask-position: center;
+            mask-size: contain;
+            mask-repeat: no-repeat;
+            background-color: $secondary-content;
+        }
     }
 
-    &:hover::after {
-        opacity: 1;
-        transform: translateX(0);
+    &:hover, &:focus {
+        cursor: pointer;
+        border-color: $quinary-content;
+
+        .mx_ThreadInfo_chevron {
+            opacity: 1;
+            transform: translateX(0);
+        }
+    }
+
+    &::before {
+        align-self: center; // v-align the threads icon
     }
 }
 
@@ -784,25 +792,34 @@ $left-gutter: 64px;
     width: initial;
 }
 
+$threadInfoLineHeight: calc(2 * $font-12px);
+
+.mx_ThreadInfo_sender {
+    font-weight: $font-semi-bold;
+    line-height: $threadInfoLineHeight;
+}
+
 .mx_ThreadInfo_content {
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
-    padding-left: 8px;
+    margin-left: 4px;
     font-size: $font-12px;
-    line-height: calc(2 * $font-12px);
+    line-height: $threadInfoLineHeight;
     color: $secondary-content;
 }
 
 .mx_ThreadInfo_avatar {
     float: left;
+    margin-right: 8px;
 }
 
 .mx_ThreadInfo_threads-amount {
-    font-weight: 600;
+    font-weight: $font-semi-bold;
     position: relative;
     padding: 0 12px 0 8px;
     white-space: nowrap;
+    line-height: $threadInfoLineHeight;
 }
 
 .mx_EventTile[data-shape=ThreadsList] {
diff --git a/res/img/compound/chevron-right-12px.svg b/res/img/compound/chevron-right-12px.svg
new file mode 100644
index 0000000000..02f61f36ff
--- /dev/null
+++ b/res/img/compound/chevron-right-12px.svg
@@ -0,0 +1,10 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1692_80)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 10.7197C3.67678 10.4268 3.67601 9.95114 3.96795 9.6573L7.66823 5.933L3.95592 2.22069C3.66303 1.92779 3.66226 1.45215 3.9542 1.15831C4.24615 0.864473 4.72025 0.863706 5.01315 1.1566L9.25579 5.39924C9.54868 5.69213 9.54945 6.16777 9.2575 6.46161L5.02861 10.718C4.73667 11.0118 4.26256 11.0126 3.96967 10.7197Z" fill="#737D8C"/>
+</g>
+<defs>
+<clipPath id="clip0_1692_80">
+<rect width="12" height="12" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/src/components/views/rooms/ThreadSummary.tsx b/src/components/views/rooms/ThreadSummary.tsx
index 22c30dacda..47a38ff17e 100644
--- a/src/components/views/rooms/ThreadSummary.tsx
+++ b/src/components/views/rooms/ThreadSummary.tsx
@@ -61,12 +61,18 @@ const ThreadSummary = ({ mxEvent, thread }: IProps) => {
             <span className="mx_ThreadInfo_threads-amount">
                 { countSection }
             </span>
-            <ThreadMessagePreview thread={thread} />
+            <ThreadMessagePreview thread={thread} showDisplayname={!roomContext.narrow} />
+            <div className="mx_ThreadInfo_chevron" />
         </AccessibleButton>
     );
 };
 
-export const ThreadMessagePreview = ({ thread }: Pick<IProps, "thread">) => {
+interface IPreviewProps {
+    thread: Thread;
+    showDisplayname?: boolean;
+}
+
+export const ThreadMessagePreview = ({ thread, showDisplayname = false }: IPreviewProps) => {
     const cli = useContext(MatrixClientContext);
     const lastReply = useTypedEventEmitterState(thread, ThreadEvent.Update, () => thread.replyToEvent);
     const preview = useAsyncMemo(async () => {
@@ -85,6 +91,9 @@ export const ThreadMessagePreview = ({ thread }: Pick<IProps, "thread">) => {
             height={24}
             className="mx_ThreadInfo_avatar"
         />
+        { showDisplayname && <div className="mx_ThreadInfo_sender">
+            { sender?.name ?? lastReply.getSender() }
+        </div> }
         <div className="mx_ThreadInfo_content">
             <span className="mx_ThreadInfo_message-preview">
                 { preview }