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.

191 lines
7.8 KiB
TypeScript
Raw Normal View History

2019-10-13 15:08:50 +03:00
/*
Copyright 2020-2021 Tulir Asokan <tulir@maunium.net>
2019-10-13 15:08:50 +03:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
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";
import { MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
2021-12-09 09:10:23 +00:00
import { EventType, MsgType } from "matrix-js-sdk/src/@types/event";
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";
2021-07-02 14:51:51 +02:00
import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
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";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { renderReplyTile } from "../../../events/EventTileFactory";
import { GetRelationsForEvent } from "../rooms/EventTile";
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(
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("This event could not be displayed")}
</div>
);
}
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) {
2021-08-18 09:29:44 +02:00
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()} width={16} height={16} />
<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: null,
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
}
}