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

Pillify event permalinks #10392

Merged
merged 8 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cypress/e2e/regression-tests/pills-click-in-app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe("Pills", () => {

// go back to the message room and try to click on the pill text, as a user would
cy.viewRoomByName(messageRoom);
cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_linkText")
cy.get(".mx_EventTile_body .mx_Pill .mx_Pill_text")
.should("have.css", "pointer-events", "none")
.click({ force: true }); // force is to ensure we bypass pointer-events
cy.url().should("contain", localUrl);
Expand Down
33 changes: 29 additions & 4 deletions res/css/views/elements/_Pill.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ limitations under the License.
color: $accent-fg-color !important; /* To override .markdown-body */
background-color: $pill-bg-color !important; /* To override .markdown-body */

> * {
pointer-events: none;
}

&.mx_UserPill_me,
&.mx_AtRoomPill {
background-color: $alert !important; /* To override .markdown-body */
Expand All @@ -55,12 +59,17 @@ limitations under the License.
min-width: $font-16px; /* ensure the avatar is not compressed */
}

.mx_Pill_linkText {
white-space: nowrap; /* enforce the pill text to be a single line */
&.mx_EventPill .mx_BaseAvatar {
/* Event pill avatars are inside the text. */
margin-inline-start: 0.2em;
margin-inline-end: 0.2em;
}

.mx_Pill_text {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;

pointer-events: none; /* ensure clicks on the pills go through the anchor */
white-space: nowrap;
}

a& {
Expand All @@ -69,4 +78,20 @@ limitations under the License.
overflow: hidden;
text-decoration: none !important; /* To override .markdown-body */
}

.mx_Pill_LinkIcon {
background-color: $link-external;
box-sizing: border-box;
color: $background;
height: 16px;
padding: 1px;
width: 16px;
}

.mx_Pill_UserIcon {
box-sizing: border-box;
color: $secondary-content;
height: 16px;
width: 16px;
}
}
7 changes: 7 additions & 0 deletions res/img/compound/user.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions res/themes/legacy-dark/css/_legacy-dark.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ $room-icon-unread-color: #fff;
$accent: #0dbd8b;
$alert: #ff5b55;
$links: #0086e6;
$link-external: #0467dd;
$primary-content: $primary-fg-color;
$secondary-content: $secondary-fg-color;
$tertiary-content: $tertiary-fg-color;
Expand Down
1 change: 1 addition & 0 deletions res/themes/legacy-light/css/_legacy-light.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ $presence-busy: #ff5b55;
$accent: #0dbd8b;
$alert: #ff5b55;
$links: #0086e6;
$link-external: #0467dd;
$primary-content: $primary-fg-color;
$secondary-content: $secondary-fg-color;
$tertiary-content: $tertiary-fg-color;
Expand Down
1 change: 1 addition & 0 deletions res/themes/light/css/_light.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ $space-nav: rgba($tertiary-content, 0.15);
$accent: #0dbd8b;
$alert: #ff5b55;
$links: #0086e6;
$link-external: #0467dd;

$username-variant1-color: #368bd6;
$username-variant2-color: #ac3ba8;
Expand Down
69 changes: 58 additions & 11 deletions src/components/views/elements/Pill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,24 @@ limitations under the License.
import React, { ReactElement, useState } from "react";
import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/matrix";

import { MatrixClientPeg } from "../../../MatrixClientPeg";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import Tooltip, { Alignment } from "../elements/Tooltip";
import { usePermalink } from "../../../hooks/usePermalink";
import RoomAvatar from "../avatars/RoomAvatar";
import MemberAvatar from "../avatars/MemberAvatar";
import { _t } from "../../../languageHandler";
import { Icon as LinkIcon } from "../../../../res/img/element-icons/room/composer/link.svg";
import { Icon as UserIcon } from "../../../../res/img/compound/user.svg";

export enum PillType {
UserMention = "TYPE_USER_MENTION",
RoomMention = "TYPE_ROOM_MENTION",
AtRoomMention = "TYPE_AT_ROOM_MENTION", // '@room' mention
EventInSameRoom = "TYPE_EVENT_IN_SAME_ROOM",
EventInOtherRoom = "TYPE_EVENT_IN_OTHER_ROOM",
}

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

const PillRoomAvatar: React.FC<{
shouldShowPillAvatar: boolean;
room: Room | null;
}> = ({ shouldShowPillAvatar, room }) => {
if (!shouldShowPillAvatar) {
return null;
}

if (room) {
return <RoomAvatar room={room} width={16} height={16} aria-hidden="true" />;
}
return <LinkIcon className="mx_Pill_LinkIcon mx_BaseAvatar mx_BaseAvatar_image" />;
};

const PillMemberAvatar: React.FC<{
shouldShowPillAvatar: boolean;
member: RoomMember | null;
}> = ({ shouldShowPillAvatar, member }) => {
if (!shouldShowPillAvatar) {
return null;
}

if (member) {
return <MemberAvatar member={member} width={16} height={16} aria-hidden="true" hideTitle />;
}
return <UserIcon className="mx_Pill_UserIcon mx_BaseAvatar mx_BaseAvatar_image" />;
};

export interface PillProps {
// The Type of this Pill. If url is given, this is auto-detected.
type?: PillType;
Expand All @@ -52,7 +86,7 @@ export interface PillProps {
shouldShowPillAvatar?: boolean;
}

export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room, shouldShowPillAvatar }) => {
export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room, shouldShowPillAvatar = true }) => {
const [hover, setHover] = useState(false);
const { member, onClick, resourceId, targetRoom, text, type } = usePermalink({
room,
Expand All @@ -70,6 +104,7 @@ export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room
mx_SpacePill: type === "space",
mx_UserPill: type === PillType.UserMention,
mx_UserPill_me: resourceId === MatrixClientPeg.get().getUserId(),
mx_EventPill: type === PillType.EventInOtherRoom || type === PillType.EventInSameRoom,
});

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

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

switch (type) {
case PillType.EventInOtherRoom:
{
const avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
content = [_t("Message in"), avatar || " ", textElement];
}
break;
case PillType.EventInSameRoom:
{
const avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
content = [_t("Message from"), avatar || " ", textElement];
}
break;
case PillType.AtRoomMention:
case PillType.RoomMention:
case "space":
avatar = targetRoom ? <RoomAvatar room={targetRoom} width={16} height={16} aria-hidden="true" /> : null;
{
const avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
content = [avatar, textElement];
}
break;
case PillType.UserMention:
avatar = <MemberAvatar member={member} width={16} height={16} aria-hidden="true" hideTitle />;
{
const avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
content = [avatar, textElement];
}
break;
default:
return null;
}

const content = (
<>
{shouldShowPillAvatar && avatar}
<span className="mx_Pill_linkText">{text}</span>
</>
);

return (
<bdi>
<MatrixClientContext.Provider value={MatrixClientPeg.get()}>
Expand Down
Loading