/* Copyright 2024 New Vector Ltd. Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Copyright 2015, 2016 , 2019, 2023 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React from "react"; import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import SyntaxHighlight from "../views/elements/SyntaxHighlight"; import { _t } from "../../languageHandler"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import { canEditContent } from "../../utils/EventUtils"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import BaseDialog from "../views/dialogs/BaseDialog"; import { DevtoolsContext } from "../views/dialogs/devtools/BaseTool"; import { StateEventEditor } from "../views/dialogs/devtools/RoomState"; import { stringify, TimelineEventEditor } from "../views/dialogs/devtools/Event"; import CopyableText from "../views/elements/CopyableText"; interface IProps { mxEvent: MatrixEvent; // the MatrixEvent associated with the context menu ignoreEdits?: boolean; onFinished(): void; } interface IState { isEditing: boolean; } export default class ViewSource extends React.Component { public constructor(props: IProps) { super(props); this.state = { isEditing: false, }; } private onBack = (): void => { // TODO: refresh the "Event ID:" modal header this.setState({ isEditing: false }); }; private onEdit(): void { this.setState({ isEditing: true }); } // returns the dialog body for viewing the event source private viewSourceContent(): JSX.Element { let mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit if (this.props.ignoreEdits) { mxEvent = this.props.mxEvent; } const isEncrypted = mxEvent.isEncrypted(); // @ts-ignore const decryptedEventSource = mxEvent.clearEvent; // FIXME: clearEvent is private const originalEventSource = mxEvent.event; const copyOriginalFunc = (): string => { return stringify(originalEventSource); }; if (isEncrypted) { const copyDecryptedFunc = (): string => { return stringify(decryptedEventSource || {}); }; return ( <>
{_t("devtools|view_source_decrypted_event_source")} {decryptedEventSource ? ( {stringify(decryptedEventSource)} ) : (
{_t("devtools|view_source_decrypted_event_source_unavailable")}
)}
{_t("devtools|original_event_source")} {stringify(originalEventSource)}
); } else { return ( <>
{_t("devtools|original_event_source")}
{stringify(originalEventSource)} ); } } // returns the SendCustomEvent component prefilled with the correct details private editSourceContent(): JSX.Element { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isStateEvent = mxEvent.isState(); const roomId = mxEvent.getRoomId(); if (isStateEvent) { return ( {(cli) => ( )} ); } return ( {(cli) => ( )} ); } private canSendStateEvent(mxEvent: MatrixEvent): boolean { const cli = MatrixClientPeg.safeGet(); const room = cli.getRoom(mxEvent.getRoomId()); return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli); } public render(): React.ReactNode { const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit const isEditing = this.state.isEditing; const roomId = mxEvent.getRoomId()!; const eventId = mxEvent.getId()!; const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) : canEditContent(MatrixClientPeg.safeGet(), this.props.mxEvent); return (
roomId} border={false}> {_t("devtools|room_id", { roomId })} eventId} border={false}> {_t("devtools|event_id", { eventId })} {mxEvent.threadRootId && ( mxEvent.threadRootId!} border={false}> {_t("devtools|thread_root_id", { threadRootId: mxEvent.threadRootId, })} )}
{isEditing ? this.editSourceContent() : this.viewSourceContent()} {!isEditing && canEdit && (
)}
); } }