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

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

188 lines
6.8 KiB
TypeScript
Raw Normal View History

2019-06-03 14:31:09 +02:00
/*
2024-09-09 14:57:16 +01:00
Copyright 2024 New Vector Ltd.
2019-06-03 14:31:09 +02:00
Copyright 2019 The Matrix.org Foundation C.I.C.
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-06-03 14:31:09 +02:00
*/
2019-06-26 16:13:15 +02:00
import React, { type JSX, createRef } from "react";
2025-02-05 13:25:06 +00:00
import { type EventStatus, type IContent, type MatrixEvent, MatrixEventEvent, MsgType } from "matrix-js-sdk/src/matrix";
2021-10-22 17:23:32 -05:00
import classNames from "classnames";
import EventContentBody from "./EventContentBody.tsx";
import { editBodyDiffToHtml } from "../../../utils/MessageDiffUtils";
2021-06-29 13:11:58 +01:00
import { formatTime } from "../../../DateUtils";
2019-07-04 11:07:22 +02:00
import { _t } from "../../../languageHandler";
2019-07-04 15:39:36 +02:00
import Modal from "../../../Modal";
2020-04-24 15:07:39 +01:00
import RedactedBody from "./RedactedBody";
2021-09-24 19:55:52 +02:00
import AccessibleButton from "../elements/AccessibleButton";
import ConfirmAndWaitRedactDialog from "../dialogs/ConfirmAndWaitRedactDialog";
import ViewSource from "../../structures/ViewSource";
import SettingsStore from "../../../settings/SettingsStore";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
function getReplacedContent(event: MatrixEvent): IContent {
const originalContent = event.getOriginalContent();
return originalContent["m.new_content"] || originalContent;
}
2021-09-24 19:55:52 +02:00
interface IProps {
// the message event being edited
mxEvent: MatrixEvent;
previousEdit?: MatrixEvent;
isBaseEvent?: boolean;
isTwelveHour?: boolean;
}
2019-06-03 14:31:09 +02:00
2021-09-24 19:55:52 +02:00
interface IState {
canRedact: boolean;
sendStatus: EventStatus | null;
2021-09-24 19:55:52 +02:00
}
export default class EditHistoryMessage extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext;
2024-12-02 09:39:36 +00:00
declare public context: React.ContextType<typeof MatrixClientContext>;
2021-09-24 19:55:52 +02:00
private content = createRef<HTMLDivElement>();
public constructor(props: IProps, context: React.ContextType<typeof MatrixClientContext>) {
2024-08-01 13:01:05 +01:00
super(props, context);
2021-09-24 19:55:52 +02:00
const cli = this.context;
const userId = cli.getSafeUserId();
2019-07-04 15:39:36 +02:00
const event = this.props.mxEvent;
const room = cli.getRoom(event.getRoomId());
event.localRedactionEvent()?.on(MatrixEventEvent.Status, this.onAssociatedStatusChanged);
const canRedact = room?.currentState.maySendRedactionForEvent(event, userId) ?? false;
2021-06-29 13:11:58 +01:00
this.state = { canRedact, sendStatus: event.getAssociatedStatus() };
2019-07-04 15:39:36 +02:00
}
2021-09-24 19:55:52 +02:00
private onAssociatedStatusChanged = (): void => {
2021-06-29 13:11:58 +01:00
this.setState({ sendStatus: this.props.mxEvent.getAssociatedStatus() });
};
2021-09-24 19:55:52 +02:00
private onRedactClick = async (): Promise<void> => {
2019-07-04 15:39:36 +02:00
const event = this.props.mxEvent;
const cli = this.context;
2019-07-04 15:39:36 +02:00
2022-06-14 17:51:51 +01:00
Modal.createDialog(
ConfirmAndWaitRedactDialog,
{
event,
redact: async () => {
await cli.redactEvent(event.getRoomId()!, event.getId()!);
},
2019-07-04 15:39:36 +02:00
},
"mx_Dialog_confirmredact",
);
2019-07-04 11:07:22 +02:00
};
2021-09-24 19:55:52 +02:00
private onViewSourceClick = (): void => {
2022-06-14 17:51:51 +01:00
Modal.createDialog(
ViewSource,
{
2021-03-06 11:30:31 +02:00
mxEvent: this.props.mxEvent,
ignoreEdits: true,
2019-07-05 16:11:15 +02:00
},
"mx_Dialog_viewsource",
);
};
2021-09-24 19:55:52 +02:00
public componentWillUnmount(): void {
const event = this.props.mxEvent;
event.localRedactionEvent()?.off(MatrixEventEvent.Status, this.onAssociatedStatusChanged);
}
private renderActionBar(): React.ReactNode {
// hide the button when already redacted
let redactButton: JSX.Element | undefined;
if (!this.props.mxEvent.isRedacted() && !this.props.isBaseEvent && this.state.canRedact) {
redactButton = <AccessibleButton onClick={this.onRedactClick}>{_t("action|remove")}</AccessibleButton>;
2019-07-04 15:39:36 +02:00
}
let viewSourceButton: JSX.Element | undefined;
if (SettingsStore.getValue("developerMode")) {
viewSourceButton = (
<AccessibleButton onClick={this.onViewSourceClick}>{_t("action|view_source")}</AccessibleButton>
);
}
if (!redactButton && !viewSourceButton) {
// Hide the empty MessageActionBar
return null;
} else {
// disabled remove button when not allowed
return (
<div className="mx_MessageActionBar">
{redactButton}
{viewSourceButton}
</div>
);
}
2019-07-04 11:07:22 +02:00
}
public render(): React.ReactNode {
2021-06-29 13:11:58 +01:00
const { mxEvent } = this.props;
const content = getReplacedContent(mxEvent);
2019-06-25 10:18:27 +02:00
let contentContainer;
if (mxEvent.isRedacted()) {
2020-04-24 15:07:39 +01:00
contentContainer = <RedactedBody mxEvent={this.props.mxEvent} />;
2019-06-25 10:18:27 +02:00
} else {
let contentElements;
if (this.props.previousEdit) {
contentElements = editBodyDiffToHtml(getReplacedContent(this.props.previousEdit), content);
} else {
contentElements = (
<EventContentBody
as="span"
mxEvent={mxEvent}
content={content}
highlights={[]}
stripReply
renderTooltipsForAmbiguousLinks
renderMentionPills
renderCodeBlocks
renderSpoilers
linkify
/>
);
}
if (mxEvent.getContent().msgtype === MsgType.Emote) {
const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();
contentContainer = (
2021-09-24 19:55:52 +02:00
<div className="mx_EventTile_content" ref={this.content}>
*&nbsp;
<span className="mx_MEmoteBody_sender">{name}</span>
&nbsp;{contentElements}
</div>
);
} else {
2021-09-24 19:55:52 +02:00
contentContainer = (
<div className="mx_EventTile_content" ref={this.content}>
{contentElements}
</div>
);
}
2019-06-25 10:18:27 +02:00
}
2019-06-25 10:18:27 +02:00
const timestamp = formatTime(new Date(mxEvent.getTs()), this.props.isTwelveHour);
const isSending = ["sending", "queued", "encrypting"].includes(this.state.sendStatus!);
const classes = classNames("mx_EventTile", {
2021-03-02 11:04:12 -07:00
// Note: we keep the `sending` state class for tests, not for our styles
2019-07-04 15:39:36 +02:00
mx_EventTile_sending: isSending,
});
2019-07-08 10:12:26 +02:00
return (
<li>
<div className={classes}>
<div className="mx_EventTile_line">
<span className="mx_MessageTimestamp">{timestamp}</span>
2019-07-08 10:12:26 +02:00
{contentContainer}
2021-09-24 19:55:52 +02:00
{this.renderActionBar()}
2019-07-08 10:12:26 +02:00
</div>
</div>
2019-07-08 10:12:26 +02:00
</li>
);
2019-06-03 14:31:09 +02:00
}
}