mirror of https://github.com/vector-im/riot-web
Add thread information in pinned message list (#12902)
parent
3d80eff65b
commit
a7e907e0e6
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 (
|
||||
<div className="mx_PinnedEventTile" role="listitem">
|
||||
<div>
|
||||
|
@ -97,6 +102,36 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
|
|||
permalinkCreator={permalinkCreator}
|
||||
replacingEventId={event.replacingEventId()}
|
||||
/>
|
||||
{displayThreadInfo && (
|
||||
<div className="mx_PinnedEventTile_thread">
|
||||
<ThreadIcon />
|
||||
{_t(
|
||||
"right_panel|pinned_messages|reply_thread",
|
||||
{},
|
||||
{
|
||||
link: (sub) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (!event.threadRootId) return;
|
||||
|
||||
const rootEvent = room.findEventById(event.threadRootId);
|
||||
if (!rootEvent) return;
|
||||
|
||||
dis.dispatch<ShowThreadPayload>({
|
||||
action: Action.ShowThread,
|
||||
rootEvent: rootEvent,
|
||||
push: true,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{sub}
|
||||
</button>
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1850,6 +1850,7 @@
|
|||
"other": "You can only pin up to %(count)s widgets"
|
||||
},
|
||||
"menu": "Open menu",
|
||||
"reply_thread": "Reply to a <link>thread message</link>",
|
||||
"title": "Pinned messages",
|
||||
"unpin_all": {
|
||||
"button": "Unpin all messages",
|
||||
|
|
|
@ -97,6 +97,36 @@ describe("<PinnedEventTile />", () => {
|
|||
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,
|
||||
|
|
|
@ -65,10 +65,88 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
|
|||
</div>
|
||||
`;
|
||||
|
||||
exports[`<PinnedEventTile /> should render pinned event with thread info 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="mx_PinnedEventTile"
|
||||
role="listitem"
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="_avatar_mcap2_17 mx_BaseAvatar mx_PinnedEventTile_senderAvatar _avatar-imageless_mcap2_61"
|
||||
data-color="2"
|
||||
data-testid="avatar-img"
|
||||
data-type="round"
|
||||
role="presentation"
|
||||
style="--cpd-avatar-size: 32px;"
|
||||
>
|
||||
a
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_wrapper"
|
||||
>
|
||||
<div
|
||||
class="mx_PinnedEventTile_top"
|
||||
>
|
||||
<span
|
||||
class="_typography_yh5dq_162 _font-body-md-semibold_yh5dq_64 mx_PinnedEventTile_sender mx_Username_color2"
|
||||
>
|
||||
@alice:server.org
|
||||
</span>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="menu"
|
||||
aria-label="Open menu"
|
||||
class="_icon-button_bh2qc_17"
|
||||
data-state="closed"
|
||||
id="radix-2"
|
||||
role="button"
|
||||
style="--cpd-icon-button-size: 24px;"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
class="_indicator-icon_133tf_26"
|
||||
style="--cpd-icon-button-size: 100%;"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mx_MTextBody mx_EventTile_content"
|
||||
>
|
||||
<div
|
||||
class="mx_EventTile_body translate"
|
||||
dir="auto"
|
||||
>
|
||||
First pinned message
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_PinnedEventTile_thread"
|
||||
>
|
||||
<div />
|
||||
<span>
|
||||
Reply to a
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
thread message
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
|
||||
<div
|
||||
aria-label="Open menu"
|
||||
aria-labelledby="radix-6"
|
||||
aria-labelledby="radix-8"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
|
@ -77,7 +155,7 @@ exports[`<PinnedEventTile /> 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[`<PinnedEventTile /> should render the menu with all the options 1`] = `
|
|||
exports[`<PinnedEventTile /> should render the menu without unpin and delete 1`] = `
|
||||
<div
|
||||
aria-label="Open menu"
|
||||
aria-labelledby="radix-2"
|
||||
aria-labelledby="radix-4"
|
||||
aria-orientation="vertical"
|
||||
class="_menu_1x5h1_17"
|
||||
data-align="start"
|
||||
|
@ -235,7 +313,7 @@ exports[`<PinnedEventTile /> 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"
|
||||
|
|
Loading…
Reference in New Issue