2017-07-21 13:41:23 +01:00
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2024 New Vector Ltd.
|
|
|
|
|
Copyright 2017-2019 , 2021 The Matrix.org Foundation C.I.C.
|
2017-07-21 13:41:23 +01:00
|
|
|
|
2024-09-09 14:57:16 +01:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|
|
|
|
Please see LICENSE files in the repository root for full details.
|
2017-07-21 13:41:23 +01:00
|
|
|
*/
|
2022-05-05 11:13:09 +02:00
|
|
|
|
2023-12-21 15:21:41 +00:00
|
|
|
import React, { ReactElement } from "react";
|
2017-07-21 13:41:23 +01:00
|
|
|
import classNames from "classnames";
|
2023-08-04 08:36:16 +01:00
|
|
|
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
2023-12-21 15:21:41 +00:00
|
|
|
import { Tooltip } from "@vector-im/compound-web";
|
2024-10-04 13:42:23 +01:00
|
|
|
import LinkIcon from "@vector-im/compound-design-tokens/assets/web/icons/link";
|
2021-10-22 17:23:32 -05:00
|
|
|
|
2021-06-29 13:11:58 +01:00
|
|
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
2019-12-17 17:26:12 +00:00
|
|
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
2023-03-08 13:06:50 +01:00
|
|
|
import { usePermalink } from "../../../hooks/usePermalink";
|
2023-03-09 12:48:36 +01:00
|
|
|
import RoomAvatar from "../avatars/RoomAvatar";
|
|
|
|
|
import MemberAvatar from "../avatars/MemberAvatar";
|
2023-03-21 10:23:20 +01:00
|
|
|
import { _t } from "../../../languageHandler";
|
|
|
|
|
import { Icon as UserIcon } from "../../../../res/img/compound/user.svg";
|
2017-07-21 13:41:23 +01:00
|
|
|
|
2022-05-05 11:13:09 +02:00
|
|
|
export enum PillType {
|
|
|
|
|
UserMention = "TYPE_USER_MENTION",
|
|
|
|
|
RoomMention = "TYPE_ROOM_MENTION",
|
|
|
|
|
AtRoomMention = "TYPE_AT_ROOM_MENTION", // '@room' mention
|
2023-03-21 10:23:20 +01:00
|
|
|
EventInSameRoom = "TYPE_EVENT_IN_SAME_ROOM",
|
|
|
|
|
EventInOtherRoom = "TYPE_EVENT_IN_OTHER_ROOM",
|
2022-05-05 11:13:09 +02:00
|
|
|
}
|
|
|
|
|
|
2023-03-28 10:07:49 +13:00
|
|
|
export const pillRoomNotifPos = (text: string | null): number => {
|
|
|
|
|
return text?.indexOf("@room") ?? -1;
|
2023-03-08 13:06:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const pillRoomNotifLen = (): number => {
|
|
|
|
|
return "@room".length;
|
|
|
|
|
};
|
|
|
|
|
|
2023-08-24 04:48:35 +01:00
|
|
|
const linkIcon = <LinkIcon className="mx_Pill_LinkIcon mx_BaseAvatar" />;
|
2023-03-22 13:27:24 +01:00
|
|
|
|
2023-03-21 10:23:20 +01:00
|
|
|
const PillRoomAvatar: React.FC<{
|
|
|
|
|
shouldShowPillAvatar: boolean;
|
|
|
|
|
room: Room | null;
|
|
|
|
|
}> = ({ shouldShowPillAvatar, room }) => {
|
|
|
|
|
if (!shouldShowPillAvatar) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (room) {
|
2023-08-24 04:48:35 +01:00
|
|
|
return <RoomAvatar room={room} size="16px" aria-hidden="true" />;
|
2023-03-21 10:23:20 +01:00
|
|
|
}
|
2023-03-22 13:27:24 +01:00
|
|
|
return linkIcon;
|
2023-03-21 10:23:20 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const PillMemberAvatar: React.FC<{
|
|
|
|
|
shouldShowPillAvatar: boolean;
|
|
|
|
|
member: RoomMember | null;
|
|
|
|
|
}> = ({ shouldShowPillAvatar, member }) => {
|
|
|
|
|
if (!shouldShowPillAvatar) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (member) {
|
2023-08-24 04:48:35 +01:00
|
|
|
return <MemberAvatar member={member} size="16px" aria-hidden="true" hideTitle />;
|
2023-03-21 10:23:20 +01:00
|
|
|
}
|
2023-08-24 04:48:35 +01:00
|
|
|
return <UserIcon className="mx_Pill_UserIcon mx_BaseAvatar" />;
|
2023-03-21 10:23:20 +01:00
|
|
|
};
|
|
|
|
|
|
2023-03-08 13:06:50 +01:00
|
|
|
export interface PillProps {
|
2022-05-05 11:13:09 +02:00
|
|
|
// The Type of this Pill. If url is given, this is auto-detected.
|
|
|
|
|
type?: PillType;
|
|
|
|
|
// The URL to pillify (no validation is done)
|
|
|
|
|
url?: string;
|
2023-03-08 13:06:50 +01:00
|
|
|
/** Whether the pill is in a message. It will act as a link then. */
|
2022-05-05 11:13:09 +02:00
|
|
|
inMessage?: boolean;
|
|
|
|
|
// The room in which this pill is being rendered
|
|
|
|
|
room?: Room;
|
|
|
|
|
// Whether to include an avatar in the pill
|
|
|
|
|
shouldShowPillAvatar?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-21 10:23:20 +01:00
|
|
|
export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room, shouldShowPillAvatar = true }) => {
|
2023-03-22 13:27:24 +01:00
|
|
|
const { event, member, onClick, resourceId, targetRoom, text, type } = usePermalink({
|
2023-03-08 13:06:50 +01:00
|
|
|
room,
|
|
|
|
|
type: propType,
|
|
|
|
|
url,
|
|
|
|
|
});
|
2022-05-05 11:13:09 +02:00
|
|
|
|
2023-03-09 12:48:36 +01:00
|
|
|
if (!type || !text) {
|
2023-03-08 13:06:50 +01:00
|
|
|
return null;
|
2020-08-29 12:14:16 +01:00
|
|
|
}
|
|
|
|
|
|
2023-03-08 13:06:50 +01:00
|
|
|
const classes = classNames("mx_Pill", {
|
|
|
|
|
mx_AtRoomPill: type === PillType.AtRoomMention,
|
|
|
|
|
mx_RoomPill: type === PillType.RoomMention,
|
2023-09-21 12:04:57 +01:00
|
|
|
mx_SpacePill: type === "space" || targetRoom?.isSpaceRoom(),
|
2023-03-08 13:06:50 +01:00
|
|
|
mx_UserPill: type === PillType.UserMention,
|
2023-06-15 08:46:19 +01:00
|
|
|
mx_UserPill_me: resourceId === MatrixClientPeg.safeGet().getUserId(),
|
2023-03-21 10:23:20 +01:00
|
|
|
mx_EventPill: type === PillType.EventInOtherRoom || type === PillType.EventInSameRoom,
|
2023-03-08 13:06:50 +01:00
|
|
|
});
|
2020-08-29 12:14:16 +01:00
|
|
|
|
2023-03-22 13:27:24 +01:00
|
|
|
let avatar: ReactElement | null = null;
|
|
|
|
|
let pillText: string | null = text;
|
2023-03-09 12:48:36 +01:00
|
|
|
|
|
|
|
|
switch (type) {
|
2023-03-21 10:23:20 +01:00
|
|
|
case PillType.EventInOtherRoom:
|
|
|
|
|
{
|
2023-03-22 13:27:24 +01:00
|
|
|
avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
|
2023-10-03 19:17:26 +01:00
|
|
|
pillText = _t("pill|permalink_other_room", {
|
2023-03-22 13:27:24 +01:00
|
|
|
room: text,
|
|
|
|
|
});
|
2023-03-21 10:23:20 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PillType.EventInSameRoom:
|
|
|
|
|
{
|
2023-03-22 13:27:24 +01:00
|
|
|
if (event) {
|
|
|
|
|
avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
|
2023-10-03 19:17:26 +01:00
|
|
|
pillText = _t("pill|permalink_this_room", {
|
2023-03-22 13:27:24 +01:00
|
|
|
user: text,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
avatar = linkIcon;
|
2023-08-22 18:47:33 +01:00
|
|
|
pillText = _t("common|message");
|
2023-03-22 13:27:24 +01:00
|
|
|
}
|
2023-03-21 10:23:20 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2023-03-09 12:48:36 +01:00
|
|
|
case PillType.AtRoomMention:
|
|
|
|
|
case PillType.RoomMention:
|
|
|
|
|
case "space":
|
2023-03-22 13:27:24 +01:00
|
|
|
avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
|
2023-03-09 12:48:36 +01:00
|
|
|
break;
|
|
|
|
|
case PillType.UserMention:
|
2023-03-22 13:27:24 +01:00
|
|
|
avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
|
2023-03-09 12:48:36 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-21 15:21:41 +00:00
|
|
|
const isAnchor = !!inMessage && !!url;
|
2023-03-08 13:06:50 +01:00
|
|
|
return (
|
|
|
|
|
<bdi>
|
2023-06-15 08:46:19 +01:00
|
|
|
<MatrixClientContext.Provider value={MatrixClientPeg.safeGet()}>
|
2023-12-21 15:21:41 +00:00
|
|
|
<Tooltip
|
|
|
|
|
label={resourceId ?? ""}
|
|
|
|
|
open={resourceId ? undefined : false}
|
2024-04-12 14:56:23 +02:00
|
|
|
placement="right"
|
2023-12-21 15:21:41 +00:00
|
|
|
isTriggerInteractive={isAnchor}
|
|
|
|
|
>
|
|
|
|
|
{isAnchor ? (
|
|
|
|
|
<a className={classes} href={url} onClick={onClick}>
|
|
|
|
|
{avatar}
|
|
|
|
|
<span className="mx_Pill_text">{pillText}</span>
|
|
|
|
|
</a>
|
|
|
|
|
) : (
|
|
|
|
|
<span className={classes}>
|
|
|
|
|
{avatar}
|
|
|
|
|
<span className="mx_Pill_text">{pillText}</span>
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</Tooltip>
|
2023-03-08 13:06:50 +01:00
|
|
|
</MatrixClientContext.Provider>
|
|
|
|
|
</bdi>
|
|
|
|
|
);
|
|
|
|
|
};
|