Add thread information in pinned message list (#12902)
This commit is contained in:
parent
3d80eff65b
commit
a7e907e0e6
5 changed files with 171 additions and 4 deletions
|
@ -37,5 +37,28 @@ limitations under the License.
|
||||||
white-space: nowrap;
|
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 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 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 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 classNames from "classnames";
|
||||||
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
|
@ -39,6 +40,7 @@ import { isContentActionable } from "../../../utils/EventUtils";
|
||||||
import { getForwardableEvent } from "../../../events";
|
import { getForwardableEvent } from "../../../events";
|
||||||
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
|
import { OpenForwardDialogPayload } from "../../../dispatcher/payloads/OpenForwardDialogPayload";
|
||||||
import { createRedactEventDialog } from "../dialogs/ConfirmRedactDialog";
|
import { createRedactEventDialog } from "../dialogs/ConfirmRedactDialog";
|
||||||
|
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
|
||||||
|
|
||||||
const AVATAR_SIZE = "32px";
|
const AVATAR_SIZE = "32px";
|
||||||
|
|
||||||
|
@ -69,6 +71,9 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
|
||||||
throw new Error("Pinned event unexpectedly has no sender");
|
throw new Error("Pinned event unexpectedly has no sender");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isInThread = Boolean(event.threadRootId);
|
||||||
|
const displayThreadInfo = !event.isThreadRoot && isInThread;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_PinnedEventTile" role="listitem">
|
<div className="mx_PinnedEventTile" role="listitem">
|
||||||
<div>
|
<div>
|
||||||
|
@ -97,6 +102,36 @@ export function PinnedEventTile({ event, room, permalinkCreator }: PinnedEventTi
|
||||||
permalinkCreator={permalinkCreator}
|
permalinkCreator={permalinkCreator}
|
||||||
replacingEventId={event.replacingEventId()}
|
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>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1850,6 +1850,7 @@
|
||||||
"other": "You can only pin up to %(count)s widgets"
|
"other": "You can only pin up to %(count)s widgets"
|
||||||
},
|
},
|
||||||
"menu": "Open menu",
|
"menu": "Open menu",
|
||||||
|
"reply_thread": "Reply to a <link>thread message</link>",
|
||||||
"title": "Pinned messages",
|
"title": "Pinned messages",
|
||||||
"unpin_all": {
|
"unpin_all": {
|
||||||
"button": "Unpin all messages",
|
"button": "Unpin all messages",
|
||||||
|
|
|
@ -97,6 +97,36 @@ describe("<PinnedEventTile />", () => {
|
||||||
expect(container).toMatchSnapshot();
|
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 () => {
|
it("should render the menu without unpin and delete", async () => {
|
||||||
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "mayClientSendStateEvent").mockReturnValue(
|
jest.spyOn(room.getLiveTimeline().getState(EventTimeline.FORWARDS)!, "mayClientSendStateEvent").mockReturnValue(
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -65,10 +65,88 @@ exports[`<PinnedEventTile /> should render pinned event 1`] = `
|
||||||
</div>
|
</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`] = `
|
exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
|
||||||
<div
|
<div
|
||||||
aria-label="Open menu"
|
aria-label="Open menu"
|
||||||
aria-labelledby="radix-6"
|
aria-labelledby="radix-8"
|
||||||
aria-orientation="vertical"
|
aria-orientation="vertical"
|
||||||
class="_menu_1x5h1_17"
|
class="_menu_1x5h1_17"
|
||||||
data-align="start"
|
data-align="start"
|
||||||
|
@ -77,7 +155,7 @@ exports[`<PinnedEventTile /> should render the menu with all the options 1`] = `
|
||||||
data-side="right"
|
data-side="right"
|
||||||
data-state="open"
|
data-state="open"
|
||||||
dir="ltr"
|
dir="ltr"
|
||||||
id="radix-7"
|
id="radix-9"
|
||||||
role="menu"
|
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;"
|
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"
|
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`] = `
|
exports[`<PinnedEventTile /> should render the menu without unpin and delete 1`] = `
|
||||||
<div
|
<div
|
||||||
aria-label="Open menu"
|
aria-label="Open menu"
|
||||||
aria-labelledby="radix-2"
|
aria-labelledby="radix-4"
|
||||||
aria-orientation="vertical"
|
aria-orientation="vertical"
|
||||||
class="_menu_1x5h1_17"
|
class="_menu_1x5h1_17"
|
||||||
data-align="start"
|
data-align="start"
|
||||||
|
@ -235,7 +313,7 @@ exports[`<PinnedEventTile /> should render the menu without unpin and delete 1`]
|
||||||
data-side="right"
|
data-side="right"
|
||||||
data-state="open"
|
data-state="open"
|
||||||
dir="ltr"
|
dir="ltr"
|
||||||
id="radix-3"
|
id="radix-5"
|
||||||
role="menu"
|
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;"
|
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"
|
tabindex="-1"
|
||||||
|
|
Loading…
Reference in a new issue