Files
ThreadNet-Web/src/components/views/rooms/ReplyTile.tsx
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

182 lines
7.5 KiB
TypeScript
Raw Normal View History

2019-10-13 15:08:50 +03:00
/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2020, 2021 Tulir Asokan <tulir@maunium.net>
2019-10-13 15:08:50 +03:00
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
2024-09-09 14:57:16 +01:00
Please see LICENSE files in the repository root for full details.
2019-10-13 15:08:50 +03:00
*/
2021-08-06 15:06:11 +02:00
import React, { createRef } from "react";
2020-04-10 14:23:34 +03:00
import classNames from "classnames";
2025-02-05 13:25:06 +00:00
import { type MatrixEvent, MatrixEventEvent, EventType, MsgType } from "matrix-js-sdk/src/matrix";
2021-12-09 09:10:23 +00:00
import { logger } from "matrix-js-sdk/src/logger";
2021-07-02 14:51:51 +02:00
import { _t } from "../../../languageHandler";
2020-05-25 19:33:30 +03:00
import dis from "../../../dispatcher/dispatcher";
2021-11-25 17:49:43 -03:00
import { Action } from "../../../dispatcher/actions";
2025-02-05 13:25:06 +00:00
import { type RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
2021-07-02 14:51:51 +02:00
import SenderProfile from "../messages/SenderProfile";
import MImageReplyBody from "../messages/MImageReplyBody";
2022-03-24 15:54:06 -06:00
import { isVoiceMessage } from "../../../utils/EventUtils";
import { getEventDisplayInfo } from "../../../utils/EventRenderingUtils";
import MFileBody from "../messages/MFileBody";
2022-11-29 08:49:43 +01:00
import MemberAvatar from "../avatars/MemberAvatar";
2021-08-18 09:28:44 +02:00
import MVoiceMessageBody from "../messages/MVoiceMessageBody";
2025-02-05 13:25:06 +00:00
import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { renderReplyTile } from "../../../events/EventTileFactory";
2025-02-05 13:25:06 +00:00
import { type GetRelationsForEvent } from "../rooms/EventTile";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
2019-10-13 15:08:50 +03:00
2021-07-02 14:51:51 +02:00
interface IProps {
mxEvent: MatrixEvent;
permalinkCreator?: RoomPermalinkCreator;
2021-07-14 09:28:37 +02:00
highlights?: string[];
2021-07-02 14:51:51 +02:00
highlightLink?: string;
onHeightChanged?(): void;
toggleExpandedQuote?: () => void;
getRelationsForEvent?: GetRelationsForEvent;
2021-07-02 14:51:51 +02:00
}
2019-10-13 15:08:50 +03:00
2021-07-02 14:51:51 +02:00
export default class ReplyTile extends React.PureComponent<IProps> {
2021-08-06 15:06:11 +02:00
private anchorElement = createRef<HTMLAnchorElement>();
public static defaultProps = {
2021-07-02 14:51:51 +02:00
onHeightChanged: () => {},
};
2019-10-13 15:08:50 +03:00
public componentDidMount(): void {
this.props.mxEvent.on(MatrixEventEvent.Decrypted, this.onDecrypted);
this.props.mxEvent.on(MatrixEventEvent.BeforeRedaction, this.onEventRequiresUpdate);
this.props.mxEvent.on(MatrixEventEvent.Replaced, this.onEventRequiresUpdate);
2019-10-13 15:08:50 +03:00
}
public componentWillUnmount(): void {
this.props.mxEvent.removeListener(MatrixEventEvent.Decrypted, this.onDecrypted);
this.props.mxEvent.removeListener(MatrixEventEvent.BeforeRedaction, this.onEventRequiresUpdate);
this.props.mxEvent.removeListener(MatrixEventEvent.Replaced, this.onEventRequiresUpdate);
2019-10-13 15:08:50 +03:00
}
2021-07-02 14:51:51 +02:00
private onDecrypted = (): void => {
2019-10-13 15:08:50 +03:00
this.forceUpdate();
if (this.props.onHeightChanged) {
this.props.onHeightChanged();
}
2021-07-02 14:51:51 +02:00
};
2019-10-13 15:08:50 +03:00
2021-07-13 11:57:40 +02:00
private onEventRequiresUpdate = (): void => {
// Force update when necessary - redactions and edits
2021-07-13 10:12:24 +02:00
this.forceUpdate();
};
2021-07-02 14:51:51 +02:00
private onClick = (e: React.MouseEvent): void => {
2021-07-27 17:57:46 +02:00
const clickTarget = e.target as HTMLElement;
// Following a link within a reply should not dispatch the `view_room` action
// so that the browser can direct the user to the correct location
2021-07-27 17:14:39 +01:00
// The exception being the link wrapping the reply
2021-08-06 15:06:11 +02:00
if (
clickTarget.tagName.toLowerCase() !== "a" ||
clickTarget.closest("a") === null ||
clickTarget === this.anchorElement.current
) {
2021-07-27 17:57:46 +02:00
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Riot when clicked.
e.preventDefault();
// Expand thread on shift key
if (this.props.toggleExpandedQuote && e.shiftKey) {
this.props.toggleExpandedQuote();
} else {
dis.dispatch<ViewRoomPayload>({
2021-11-25 17:49:43 -03:00
action: Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
metricsTrigger: undefined, // room doesn't change
});
}
2021-07-27 17:57:46 +02:00
}
2021-07-02 14:51:51 +02:00
};
2019-10-13 15:08:50 +03:00
public render(): React.ReactNode {
2021-07-14 12:15:13 +02:00
const mxEvent = this.props.mxEvent;
2021-07-15 15:17:41 +02:00
const msgType = mxEvent.getContent().msgtype;
2022-11-29 08:49:43 +01:00
const evType = mxEvent.getType();
2019-10-13 15:08:50 +03:00
const { hasRenderer, isInfoMessage, isSeeingThroughMessageHiddenForModeration } = getEventDisplayInfo(
MatrixClientPeg.safeGet(),
mxEvent,
false /* Replies are never hidden, so this should be fine */,
);
2019-10-13 15:08:50 +03:00
// This shouldn't happen: the caller should check we support this type
// before trying to instantiate us
if (!hasRenderer) {
2021-07-02 12:56:08 +03:00
const { mxEvent } = this.props;
2021-10-15 16:31:29 +02:00
logger.warn(`Event type not supported: type:${mxEvent.getType()} isState:${mxEvent.isState()}`);
2019-10-13 15:08:50 +03:00
return (
<div className="mx_ReplyTile mx_ReplyTile_info mx_MNoticeBody">{_t("timeline|error_no_renderer")}</div>
2019-10-13 15:08:50 +03:00
);
}
2021-07-02 14:51:51 +02:00
2021-07-13 09:27:22 +02:00
const classes = classNames("mx_ReplyTile", {
mx_ReplyTile_inline: msgType === MsgType.Emote,
2021-08-18 09:29:44 +02:00
mx_ReplyTile_info: isInfoMessage && !mxEvent.isRedacted(),
2021-07-15 15:17:41 +02:00
mx_ReplyTile_audio: msgType === MsgType.Audio,
mx_ReplyTile_video: msgType === MsgType.Video,
2019-10-13 15:08:50 +03:00
});
let permalink = "#";
if (this.props.permalinkCreator) {
permalink = this.props.permalinkCreator.forEvent(mxEvent.getId()!);
2019-10-13 15:08:50 +03:00
}
let sender;
2022-11-29 08:49:43 +01:00
const hasOwnSender = isInfoMessage || evType === EventType.RoomCreate;
if (!hasOwnSender) {
sender = (
<div className="mx_ReplyTile_sender">
<MemberAvatar member={mxEvent.sender} fallbackUserId={mxEvent.getSender()} size="16px" />
2022-11-29 08:49:43 +01:00
<SenderProfile mxEvent={mxEvent} />
</div>
);
2019-10-13 15:08:50 +03:00
}
const msgtypeOverrides: Record<string, typeof React.Component> = {
2021-07-13 09:30:52 +02:00
[MsgType.Image]: MImageReplyBody,
// Override audio and video body with file body. We also hide the download/decrypt button using CSS
2021-08-18 09:28:44 +02:00
[MsgType.Audio]: isVoiceMessage(mxEvent) ? MVoiceMessageBody : MFileBody,
[MsgType.Video]: MFileBody,
2019-10-13 15:08:50 +03:00
};
const evOverrides: Record<string, typeof React.Component> = {
2021-07-14 12:15:13 +02:00
// Use MImageReplyBody so that the sticker isn't taking up a lot of space
[EventType.Sticker]: MImageReplyBody,
2019-10-13 15:08:50 +03:00
};
return (
<div className={classes}>
2021-08-06 15:06:11 +02:00
<a href={permalink} onClick={this.onClick} ref={this.anchorElement}>
2019-10-13 15:08:50 +03:00
{sender}
{renderReplyTile(
{
...this.props,
// overrides
ref: undefined,
showUrlPreview: false,
overrideBodyTypes: msgtypeOverrides,
overrideEventTypes: evOverrides,
maxImageHeight: 96,
isSeeingThroughMessageHiddenForModeration,
// appease TS
highlights: this.props.highlights,
highlightLink: this.props.highlightLink,
onHeightChanged: this.props.onHeightChanged,
permalinkCreator: this.props.permalinkCreator,
},
false /* showHiddenEvents shouldn't be relevant */,
)}
2019-10-13 15:08:50 +03:00
</a>
</div>
2020-03-05 13:44:54 +02:00
);
2019-10-13 15:08:50 +03:00
}
}