Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 0e2342d

Browse files
committed
Implement event permalink pills
1 parent a86a8e7 commit 0e2342d

File tree

16 files changed

+553
-116
lines changed

16 files changed

+553
-116
lines changed

cypress/e2e/regression-tests/pills-click-in-app.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ describe("Pills", () => {
6767

6868
// go back to the message room and try to click on the pill text, as a user would
6969
cy.viewRoomByName(messageRoom);
70-
cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_linkText")
70+
cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_content")
7171
.should("have.css", "pointer-events", "none")
7272
.click({ force: true }); // force is to ensure we bypass pointer-events
7373
cy.url().should("contain", localUrl);

res/css/views/elements/_Pill.pcss

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,17 @@ limitations under the License.
5555
min-width: $font-16px; /* ensure the avatar is not compressed */
5656
}
5757

58-
.mx_Pill_linkText {
59-
white-space: nowrap; /* enforce the pill text to be a single line */
58+
&.mx_EventPill .mx_BaseAvatar {
59+
/* Event pill avatars are inside the text. */
60+
margin-inline-start: 0.2em;
61+
margin-inline-end: 0.2em;
62+
}
63+
64+
.mx_Pill_text {
65+
min-width: 0;
6066
overflow: hidden;
6167
text-overflow: ellipsis;
62-
63-
pointer-events: none; /* ensure clicks on the pills go through the anchor */
68+
white-space: nowrap;
6469
}
6570

6671
a& {
@@ -69,4 +74,20 @@ limitations under the License.
6974
overflow: hidden;
7075
text-decoration: none !important; /* To override .markdown-body */
7176
}
77+
78+
.mx_Pill_LinkIcon {
79+
background-color: $link-external;
80+
box-sizing: border-box;
81+
color: $background;
82+
height: 16px;
83+
padding: 1px;
84+
width: 16px;
85+
}
86+
87+
.mx_Pill_UserIcon {
88+
box-sizing: border-box;
89+
color: $secondary-content;
90+
height: 16px;
91+
width: 16px;
92+
}
7293
}

res/img/compound/user.svg

Lines changed: 7 additions & 0 deletions
Loading

res/themes/legacy-dark/css/_legacy-dark.pcss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ $room-icon-unread-color: #fff;
9090
$accent: #0dbd8b;
9191
$alert: #ff5b55;
9292
$links: #0086e6;
93+
$link-external: #0467dd;
9394
$primary-content: $primary-fg-color;
9495
$secondary-content: $secondary-fg-color;
9596
$tertiary-content: $tertiary-fg-color;

res/themes/legacy-light/css/_legacy-light.pcss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ $presence-busy: #ff5b55;
149149
$accent: #0dbd8b;
150150
$alert: #ff5b55;
151151
$links: #0086e6;
152+
$link-external: #0467dd;
152153
$primary-content: $primary-fg-color;
153154
$secondary-content: $secondary-fg-color;
154155
$tertiary-content: $tertiary-fg-color;

res/themes/light/css/_light.pcss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ $space-nav: rgba($tertiary-content, 0.15);
4141
$accent: #0dbd8b;
4242
$alert: #ff5b55;
4343
$links: #0086e6;
44+
$link-external: #0467dd;
4445

4546
$username-variant1-color: #368bd6;
4647
$username-variant2-color: #ac3ba8;

src/components/views/elements/Pill.tsx

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,24 @@ limitations under the License.
1717
import React, { ReactElement, useState } from "react";
1818
import classNames from "classnames";
1919
import { Room } from "matrix-js-sdk/src/models/room";
20+
import { RoomMember } from "matrix-js-sdk/src/matrix";
2021

2122
import { MatrixClientPeg } from "../../../MatrixClientPeg";
2223
import MatrixClientContext from "../../../contexts/MatrixClientContext";
2324
import Tooltip, { Alignment } from "../elements/Tooltip";
2425
import { usePermalink } from "../../../hooks/usePermalink";
2526
import RoomAvatar from "../avatars/RoomAvatar";
2627
import MemberAvatar from "../avatars/MemberAvatar";
28+
import { _t } from "../../../languageHandler";
29+
import { Icon as LinkIcon } from "../../../../res/img/element-icons/room/composer/link.svg";
30+
import { Icon as UserIcon } from "../../../../res/img/compound/user.svg";
2731

2832
export enum PillType {
2933
UserMention = "TYPE_USER_MENTION",
3034
RoomMention = "TYPE_ROOM_MENTION",
3135
AtRoomMention = "TYPE_AT_ROOM_MENTION", // '@room' mention
36+
EventInSameRoom = "TYPE_EVENT_IN_SAME_ROOM",
37+
EventInOtherRoom = "TYPE_EVENT_IN_OTHER_ROOM",
3238
}
3339

3440
export const pillRoomNotifPos = (text: string): number => {
@@ -39,6 +45,34 @@ export const pillRoomNotifLen = (): number => {
3945
return "@room".length;
4046
};
4147

48+
const PillRoomAvatar: React.FC<{
49+
shouldShowPillAvatar: boolean;
50+
room: Room | null;
51+
}> = ({ shouldShowPillAvatar, room }) => {
52+
if (!shouldShowPillAvatar) {
53+
return null;
54+
}
55+
56+
if (room) {
57+
return <RoomAvatar room={room} width={16} height={16} aria-hidden="true" />;
58+
}
59+
return <LinkIcon className="mx_Pill_LinkIcon mx_BaseAvatar mx_BaseAvatar_image" />;
60+
};
61+
62+
const PillMemberAvatar: React.FC<{
63+
shouldShowPillAvatar: boolean;
64+
member: RoomMember | null;
65+
}> = ({ shouldShowPillAvatar, member }) => {
66+
if (!shouldShowPillAvatar) {
67+
return null;
68+
}
69+
70+
if (member) {
71+
return <MemberAvatar member={member} width={16} height={16} aria-hidden="true" hideTitle />;
72+
}
73+
return <UserIcon className="mx_Pill_UserIcon mx_BaseAvatar mx_BaseAvatar_image" />;
74+
};
75+
4276
export interface PillProps {
4377
// The Type of this Pill. If url is given, this is auto-detected.
4478
type?: PillType;
@@ -70,6 +104,7 @@ export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room
70104
mx_SpacePill: type === "space",
71105
mx_UserPill: type === PillType.UserMention,
72106
mx_UserPill_me: resourceId === MatrixClientPeg.get().getUserId(),
107+
mx_EventPill: type === PillType.EventInOtherRoom || type === PillType.EventInSameRoom,
73108
});
74109

75110
const onMouseOver = (): void => {
@@ -81,28 +116,40 @@ export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room
81116
};
82117

83118
const tip = hover && resourceId ? <Tooltip label={resourceId} alignment={Alignment.Right} /> : null;
84-
let avatar: ReactElement | null = null;
119+
let content: (ReactElement | string)[] = [];
120+
const textElement = <span className="mx_Pill_text">{text}</span>;
85121

86122
switch (type) {
123+
case PillType.EventInOtherRoom:
124+
{
125+
const avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
126+
content = [_t("Message in"), avatar || " ", textElement];
127+
}
128+
break;
129+
case PillType.EventInSameRoom:
130+
{
131+
const avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
132+
content = [_t("Message from"), avatar || " ", textElement];
133+
}
134+
break;
87135
case PillType.AtRoomMention:
88136
case PillType.RoomMention:
89137
case "space":
90-
avatar = targetRoom ? <RoomAvatar room={targetRoom} width={16} height={16} aria-hidden="true" /> : null;
138+
{
139+
const avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
140+
content = [avatar, textElement];
141+
}
91142
break;
92143
case PillType.UserMention:
93-
avatar = <MemberAvatar member={member} width={16} height={16} aria-hidden="true" hideTitle />;
144+
{
145+
const avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
146+
content = [avatar, textElement];
147+
}
94148
break;
95149
default:
96150
return null;
97151
}
98152

99-
const content = (
100-
<>
101-
{shouldShowPillAvatar && avatar}
102-
<span className="mx_Pill_linkText">{text}</span>
103-
</>
104-
);
105-
106153
return (
107154
<bdi>
108155
<MatrixClientContext.Provider value={MatrixClientPeg.get()}>

0 commit comments

Comments
 (0)