diff --git a/res/css/views/rooms/_PinnedEventTile.pcss b/res/css/views/rooms/_PinnedEventTile.pcss index b42de75649..b37e3724fc 100644 --- a/res/css/views/rooms/_PinnedEventTile.pcss +++ b/res/css/views/rooms/_PinnedEventTile.pcss @@ -37,5 +37,28 @@ limitations under the License. white-space: nowrap; } } + + .mx_PinnedEventTile_thread { + display: flex; + gap: var(--cpd-space-2x); + font: var(--cpd-font-body-sm-regular); + + svg { + width: 20px; + fill: var(--cpd-color-icon-tertiary); + } + + span { + display: flex; + color: var(--cpd-color-text-secondary); + } + + button { + background: transparent; + border: none; + cursor: pointer; + text-decoration: underline; + } + } } } diff --git a/src/components/views/rooms/PinnedEventTile.tsx b/src/components/views/rooms/PinnedEventTile.tsx index 5252e5124d..5fb9c07f45 100644 --- a/src/components/views/rooms/PinnedEventTile.tsx +++ b/src/components/views/rooms/PinnedEventTile.tsx @@ -23,6 +23,7 @@ import { Icon as UnpinIcon } from "@vector-im/compound-design-tokens/icons/unpin import { Icon as ForwardIcon } from "@vector-im/compound-design-tokens/icons/forward.svg"; import { Icon as TriggerIcon } from "@vector-im/compound-design-tokens/icons/overflow-horizontal.svg"; import { Icon as DeleteIcon } from "@vector-im/compound-design-tokens/icons/delete.svg"; +import { Icon as ThreadIcon } from "@vector-im/compound-design-tokens/icons/threads.svg"; import classNames from "classnames"; import dis from "../../../dispatcher/dispatcher"; @@ -39,6 +40,7 @@ import { isContentActionable } from "../../../utils/EventUtils"; import { getForwardableEvent } from "../../../events"; import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload"; import { createRedactEventDialog } from "../dialogs/ConfirmRedactDialog"; +import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload"; const AVATAR_SIZE = "32px"; @@ -69,6 +71,9 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi throw new Error("Pinned event unexpectedly has no sender"); } + const isInThread = Boolean(event.threadRootId); + const displayThreadInfo = !event.isThreadRoot && isInThread; + return (
@@ -97,6 +102,36 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi permalinkCreator={permalinkCreator} replacingEventId={event.replacingEventId()} /> + {displayThreadInfo && ( +
+ + {_t( + "right_panel|pinned_messages|reply_thread", + {}, + { + link: (sub) => ( + + ), + }, + )} +
+ )}
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 1b7b1f2ed9..889fc157e9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1850,6 +1850,7 @@ "other": "You can only pin up to %(count)s widgets" }, "menu": "Open menu", + "reply_thread": "Reply to a thread message", "title": "Pinned messages", "unpin_all": { "button": "Unpin all messages", diff --git a/test/components/views/rooms/PinnedEventTile-test.tsx b/test/components/views/rooms/PinnedEventTile-test.tsx index ed27a4e495..124138d834 100644 --- a/test/components/views/rooms/PinnedEventTile-test.tsx +++ b/test/components/views/rooms/PinnedEventTile-test.tsx @@ -97,6 +97,36 @@ describe("", () => { expect(container).toMatchSnapshot(); }); + it("should render pinned event with thread info", async () => { + const event = makePinEvent({ + content: { + "body": "First pinned message", + "msgtype": "m.text", + "m.relates_to": { + "event_id": "$threadRootEventId", + "is_falling_back": true, + "m.in_reply_to": { + event_id: "$$threadRootEventId", + }, + "rel_type": "m.thread", + }, + }, + }); + const threadRootEvent = makePinEvent({ event_id: "$threadRootEventId" }); + jest.spyOn(room, "findEventById").mockReturnValue(threadRootEvent); + + const { container } = renderComponent(event); + expect(container).toMatchSnapshot(); + + await userEvent.click(screen.getByRole("button", { name: "thread message" })); + // Check that the thread is opened + expect(dis.dispatch).toHaveBeenCalledWith({ + action: Action.ShowThread, + rootEvent: threadRootEvent, + push: true, + }); + }); + it("should render the menu without unpin and delete", async () => { jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "mayClientSendStateEvent").mockReturnValue( false, diff --git a/test/components/views/rooms/__snapshots__/PinnedEventTile-test.tsx.snap b/test/components/views/rooms/__snapshots__/PinnedEventTile-test.tsx.snap index b44b6a41a0..bf8cafe2dc 100644 --- a/test/components/views/rooms/__snapshots__/PinnedEventTile-test.tsx.snap +++ b/test/components/views/rooms/__snapshots__/PinnedEventTile-test.tsx.snap @@ -65,10 +65,88 @@ exports[` should render pinned event 1`] = ` `; +exports[` should render pinned event with thread info 1`] = ` +
+
+
+ + a + +
+
+
+ + @alice:server.org + + +
+
+
+ First pinned message +
+
+
+
+ + Reply to a + + +
+
+
+
+`; + exports[` should render the menu with all the options 1`] = `
should render the menu with all the options 1`] = ` data-side="right" data-state="open" dir="ltr" - id="radix-7" + id="radix-9" role="menu" style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;" tabindex="-1" @@ -226,7 +304,7 @@ exports[` should render the menu with all the options 1`] = ` exports[` should render the menu without unpin and delete 1`] = `
should render the menu without unpin and delete 1`] data-side="right" data-state="open" dir="ltr" - id="radix-3" + id="radix-5" role="menu" style="outline: none; --radix-dropdown-menu-content-transform-origin: var(--radix-popper-transform-origin); --radix-dropdown-menu-content-available-width: var(--radix-popper-available-width); --radix-dropdown-menu-content-available-height: var(--radix-popper-available-height); --radix-dropdown-menu-trigger-width: var(--radix-popper-anchor-width); --radix-dropdown-menu-trigger-height: var(--radix-popper-anchor-height); pointer-events: auto;" tabindex="-1"