Files
ThreadNet-Web/src/components/views/messages/MessageEvent.tsx
T

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

215 lines
8.6 KiB
TypeScript
Raw Normal View History

/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
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.
*/
2021-06-29 13:11:58 +01:00
import React, { createRef } from "react";
import {
EventType,
MsgType,
MatrixEventEvent,
M_BEACON_INFO,
M_LOCATION,
M_POLL_END,
M_POLL_START,
} from "matrix-js-sdk/src/matrix";
2021-10-22 17:23:32 -05:00
2019-10-31 16:19:42 -06:00
import SettingsStore from "../../../settings/SettingsStore";
2021-06-29 13:11:58 +01:00
import { Mjolnir } from "../../../mjolnir/Mjolnir";
2020-04-24 15:07:39 +01:00
import RedactedBody from "./RedactedBody";
2020-05-08 19:15:59 +01:00
import UnknownBody from "./UnknownBody";
import { IMediaBody } from "./IMediaBody";
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
2021-07-16 10:57:14 -06:00
import { IBodyProps } from "./IBodyProps";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
2022-03-02 16:33:40 -07:00
import TextualBody from "./TextualBody";
import MImageBody from "./MImageBody";
import MFileBody from "./MFileBody";
import MVoiceOrAudioBody from "./MVoiceOrAudioBody";
import MVideoBody from "./MVideoBody";
import MStickerBody from "./MStickerBody";
import MPollBody from "./MPollBody";
2023-02-08 10:12:39 +13:00
import { MPollEndBody } from "./MPollEndBody";
2022-03-02 16:33:40 -07:00
import MLocationBody from "./MLocationBody";
import MjolnirBody from "./MjolnirBody";
import MBeaconBody from "./MBeaconBody";
import { DecryptionFailureBody } from "./DecryptionFailureBody";
import { GetRelationsForEvent, IEventTileOps } from "../rooms/EventTile";
2022-09-26 15:29:38 +02:00
import { VoiceBroadcastBody, VoiceBroadcastInfoEventType, VoiceBroadcastInfoState } from "../../../voice-broadcast";
2021-07-16 10:57:14 -06:00
// onMessageAllowed is handled internally
interface IProps extends Omit<IBodyProps, "onMessageAllowed" | "mediaEventHelper"> {
/* overrides for the msgtype-specific components, used by ReplyTile to override file rendering */
overrideBodyTypes?: Record<string, typeof React.Component>;
overrideEventTypes?: Record<string, typeof React.Component>;
2021-11-23 10:27:44 +00:00
// helper function to access relations for this event
getRelationsForEvent?: GetRelationsForEvent;
isSeeingThroughMessageHiddenForModeration?: boolean;
}
2022-04-19 09:59:23 +02:00
export interface IOperableEventTile {
getEventTileOps(): IEventTileOps | null;
2022-04-19 09:59:23 +02:00
}
2022-08-29 17:02:24 -06:00
const baseBodyTypes = new Map<string, typeof React.Component>([
[MsgType.Text, TextualBody],
[MsgType.Notice, TextualBody],
[MsgType.Emote, TextualBody],
[MsgType.Image, MImageBody],
[MsgType.File, MFileBody],
[MsgType.Audio, MVoiceOrAudioBody],
[MsgType.Video, MVideoBody],
]);
2023-07-07 13:37:26 +01:00
const baseEvTypes = new Map<string, React.ComponentType<IBodyProps>>([
2022-08-29 17:02:24 -06:00
[EventType.Sticker, MStickerBody],
[M_POLL_START.name, MPollBody],
[M_POLL_START.altName, MPollBody],
2023-02-08 10:12:39 +13:00
[M_POLL_END.name, MPollEndBody],
[M_POLL_END.altName, MPollEndBody],
2022-08-29 17:02:24 -06:00
[M_BEACON_INFO.name, MBeaconBody],
[M_BEACON_INFO.altName, MBeaconBody],
]);
2022-10-19 12:04:15 +02:00
export default class MessageEvent extends React.Component<IProps> implements IMediaBody, IOperableEventTile {
private body: React.RefObject<React.Component | IOperableEventTile> = createRef();
private mediaHelper?: MediaEventHelper;
2022-08-29 17:02:24 -06:00
private bodyTypes = new Map<string, typeof React.Component>(baseBodyTypes.entries());
2023-07-07 13:37:26 +01:00
private evTypes = new Map<string, React.ComponentType<IBodyProps>>(baseEvTypes.entries());
2016-02-17 19:50:04 +00:00
2022-08-29 17:02:24 -06:00
public static contextType = MatrixClientContext;
2024-08-01 17:14:28 +01:00
public declare context: React.ContextType<typeof MatrixClientContext>;
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
super(props, context);
2016-02-17 19:50:04 +00:00
if (MediaEventHelper.isEligible(this.props.mxEvent)) {
this.mediaHelper = new MediaEventHelper(this.props.mxEvent);
}
2022-08-29 17:02:24 -06:00
this.updateComponentMaps();
2020-08-29 12:14:16 +01:00
}
2019-12-08 12:16:17 +00:00
public componentDidMount(): void {
this.props.mxEvent.addListener(MatrixEventEvent.Decrypted, this.onDecrypted);
}
public componentWillUnmount(): void {
this.props.mxEvent.removeListener(MatrixEventEvent.Decrypted, this.onDecrypted);
this.mediaHelper?.destroy();
}
2016-02-17 19:50:04 +00:00
public componentDidUpdate(prevProps: Readonly<IProps>): void {
if (this.props.mxEvent !== prevProps.mxEvent && MediaEventHelper.isEligible(this.props.mxEvent)) {
this.mediaHelper?.destroy();
this.mediaHelper = new MediaEventHelper(this.props.mxEvent);
}
2022-08-29 17:02:24 -06:00
this.updateComponentMaps();
}
2019-10-31 16:19:42 -06:00
private updateComponentMaps(): void {
2022-08-29 17:02:24 -06:00
this.bodyTypes = new Map<string, typeof React.Component>(baseBodyTypes.entries());
for (const [bodyType, bodyComponent] of Object.entries(this.props.overrideBodyTypes ?? {})) {
this.bodyTypes.set(bodyType, bodyComponent);
}
2023-07-07 13:37:26 +01:00
this.evTypes = new Map<string, React.ComponentType<IBodyProps>>(baseEvTypes.entries());
2022-08-29 17:02:24 -06:00
for (const [evType, evComponent] of Object.entries(this.props.overrideEventTypes ?? {})) {
this.evTypes.set(evType, evComponent);
}
}
public getEventTileOps = (): IEventTileOps | null => {
return (this.body.current as IOperableEventTile)?.getEventTileOps?.() || null;
};
public getMediaHelper(): MediaEventHelper | undefined {
return this.mediaHelper;
}
private onDecrypted = (): void => {
// Recheck MediaEventHelper eligibility as it can change when the event gets decrypted
if (MediaEventHelper.isEligible(this.props.mxEvent)) {
this.mediaHelper?.destroy();
this.mediaHelper = new MediaEventHelper(this.props.mxEvent);
}
};
private onTileUpdate = (): void => {
this.forceUpdate();
};
public render(): React.ReactNode {
2017-10-11 17:56:17 +01:00
const content = this.props.mxEvent.getContent();
2018-06-13 09:32:21 +01:00
const type = this.props.mxEvent.getType();
2017-10-11 17:56:17 +01:00
const msgtype = content.msgtype;
2023-07-07 13:37:26 +01:00
let BodyType: React.ComponentType<IBodyProps> = RedactedBody;
2018-06-13 09:32:21 +01:00
if (!this.props.mxEvent.isRedacted()) {
// only resolve BodyType if event is not redacted
if (this.props.mxEvent.isDecryptionFailure()) {
BodyType = DecryptionFailureBody;
} else if (type && this.evTypes.has(type)) {
BodyType = this.evTypes.get(type)!;
2022-08-29 17:02:24 -06:00
} else if (msgtype && this.bodyTypes.has(msgtype)) {
BodyType = this.bodyTypes.get(msgtype)!;
2018-06-13 09:32:21 +01:00
} else if (content.url) {
// Fallback to MFileBody if there's a content URL
BodyType = this.bodyTypes.get(MsgType.File)!;
2020-05-08 19:15:59 +01:00
} else {
// Fallback to UnknownBody otherwise if not redacted
BodyType = UnknownBody;
2018-06-13 09:32:21 +01:00
}
2022-02-02 11:44:17 +00:00
// TODO: move to eventTypes when location sharing spec stabilises
if (M_LOCATION.matches(type) || (type === EventType.RoomMessage && msgtype === MsgType.Location)) {
2022-03-02 16:33:40 -07:00
BodyType = MLocationBody;
2021-12-06 09:45:12 +00:00
}
2022-09-26 15:29:38 +02:00
if (type === VoiceBroadcastInfoEventType && content?.state === VoiceBroadcastInfoState.Started) {
2022-09-26 15:29:38 +02:00
BodyType = VoiceBroadcastBody;
}
}
if (SettingsStore.getValue("feature_mjolnir")) {
2019-10-31 16:27:45 -06:00
const key = `mx_mjolnir_render_${this.props.mxEvent.getRoomId()}__${this.props.mxEvent.getId()}`;
const allowRender = localStorage.getItem(key) === "true";
2019-10-31 16:19:42 -06:00
if (!allowRender) {
const userDomain = this.props.mxEvent.getSender()?.split(":").slice(1).join(":");
const userBanned = Mjolnir.sharedInstance().isUserBanned(this.props.mxEvent.getSender()!);
const serverBanned = userDomain && Mjolnir.sharedInstance().isServerBanned(userDomain);
2019-10-31 16:19:42 -06:00
if (userBanned || serverBanned) {
2022-03-02 16:33:40 -07:00
BodyType = MjolnirBody;
2019-10-31 16:19:42 -06:00
}
}
}
2019-10-13 15:08:50 +03:00
return BodyType ? (
<BodyType
ref={this.body}
2019-12-08 12:16:17 +00:00
mxEvent={this.props.mxEvent}
2018-01-04 22:21:38 +00:00
highlights={this.props.highlights}
highlightLink={this.props.highlightLink}
showUrlPreview={this.props.showUrlPreview}
forExport={this.props.forExport}
maxImageHeight={this.props.maxImageHeight}
replacingEventId={this.props.replacingEventId}
editState={this.props.editState}
2019-10-31 16:19:42 -06:00
onHeightChanged={this.props.onHeightChanged}
2019-11-06 10:52:00 -07:00
onMessageAllowed={this.onTileUpdate}
2021-02-24 19:11:08 +01:00
permalinkCreator={this.props.permalinkCreator}
mediaEventHelper={this.mediaHelper}
2021-11-23 10:27:44 +00:00
getRelationsForEvent={this.props.getRelationsForEvent}
isSeeingThroughMessageHiddenForModeration={this.props.isSeeingThroughMessageHiddenForModeration}
inhibitInteraction={this.props.inhibitInteraction}
2020-03-05 13:44:54 +02:00
/>
) : null;
2020-08-29 12:14:16 +01:00
}
}