diff --git a/src/components/views/dialogs/devtools/BaseTool.tsx b/src/components/views/dialogs/devtools/BaseTool.tsx index 473c0255e27..1cbcd89f409 100644 --- a/src/components/views/dialogs/devtools/BaseTool.tsx +++ b/src/components/views/dialogs/devtools/BaseTool.tsx @@ -31,6 +31,7 @@ export interface IDevtoolsProps { interface IMinProps extends Pick { className?: string; children?: ReactNode; + extraButton?: ReactNode; } interface IProps extends IMinProps { @@ -38,7 +39,14 @@ interface IProps extends IMinProps { onAction(): Promise; } -const BaseTool: React.FC> = ({ className, actionLabel, onBack, onAction, children }) => { +const BaseTool: React.FC> = ({ + className, + actionLabel, + onBack, + onAction, + children, + extraButton, +}) => { const [message, setMessage] = useState(null); const onBackClick = (): void => { @@ -68,6 +76,7 @@ const BaseTool: React.FC> = ({ className, actionLabel, on <>
{children}
+ {extraButton} {actionButton}
diff --git a/src/components/views/dialogs/devtools/Event.tsx b/src/components/views/dialogs/devtools/Event.tsx index c4d8af09a86..6d09357463f 100644 --- a/src/components/views/dialogs/devtools/Event.tsx +++ b/src/components/views/dialogs/devtools/Event.tsx @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ChangeEvent, useContext, useMemo, useRef, useState } from "react"; +import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react"; import { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t, _td } from "../../../../languageHandler"; @@ -143,9 +143,10 @@ export interface IEditorProps extends Pick { interface IViewerProps extends Required { Editor: React.FC; + extraButton?: ReactNode; } -export const EventViewer: React.FC = ({ mxEvent, onBack, Editor }) => { +export const EventViewer: React.FC = ({ mxEvent, onBack, Editor, extraButton }) => { const [editing, setEditing] = useState(false); if (editing) { @@ -160,7 +161,7 @@ export const EventViewer: React.FC = ({ mxEvent, onBack, Editor }) }; return ( - + {stringify(mxEvent.event)} ); diff --git a/src/components/views/dialogs/devtools/RoomState.tsx b/src/components/views/dialogs/devtools/RoomState.tsx index 141a091467e..3c6ac3a9351 100644 --- a/src/components/views/dialogs/devtools/RoomState.tsx +++ b/src/components/views/dialogs/devtools/RoomState.tsx @@ -24,6 +24,9 @@ import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import { EventEditor, EventViewer, eventTypeField, stateKeyField, IEditorProps, stringify } from "./Event"; import FilteredList from "./FilteredList"; +import Spinner from "../../elements/Spinner"; +import SyntaxHighlight from "../../elements/SyntaxHighlight"; +import { useAsyncMemo } from "../../../../hooks/useAsyncMemo"; export const StateEventEditor: React.FC = ({ mxEvent, onBack }) => { const context = useContext(DevtoolsContext); @@ -47,6 +50,48 @@ interface StateEventButtonProps { onClick(): void; } +const RoomStateHistory: React.FC<{ + mxEvent: MatrixEvent; + onBack(): void; +}> = ({ mxEvent, onBack }) => { + const cli = useContext(MatrixClientContext); + const events = useAsyncMemo( + async () => { + const events = [mxEvent.event]; + while (!!events[0].unsigned?.replaces_state) { + try { + events.unshift(await cli.fetchRoomEvent(mxEvent.getRoomId()!, events[0].unsigned.replaces_state)); + } catch (e) { + events.unshift({ + event_id: events[0].unsigned.replaces_state, + unsigned: { + error: e instanceof Error ? e.message : String(e), + }, + }); + } + } + return events; + }, + [cli, mxEvent], + null, + ); + + let body = ; + if (events !== null) { + body = ( + <> + {events.map((ev) => ( + + {stringify(ev)} + + ))} + + ); + } + + return {body}; +}; + const StateEventButton: React.FC = ({ label, onClick }) => { const trimmed = label.trim(); @@ -71,6 +116,7 @@ const RoomStateExplorerEventType: React.FC = ({ eventType, onBa const context = useContext(DevtoolsContext); const [query, setQuery] = useState(""); const [event, setEvent] = useState(null); + const [history, setHistory] = useState(false); const events = context.room.currentState.events.get(eventType)!; @@ -82,6 +128,12 @@ const RoomStateExplorerEventType: React.FC = ({ eventType, onBa } }, [events]); + if (event && history) { + const _onBack = (): void => { + setHistory(false); + }; + return ; + } if (event) { const _onBack = (): void => { if (events?.size === 1 && events.has("")) { @@ -90,7 +142,11 @@ const RoomStateExplorerEventType: React.FC = ({ eventType, onBa setEvent(null); } }; - return ; + const onHistoryClick = (): void => { + setHistory(true); + }; + const extraButton = ; + return ; } return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 5c51a0f7fce..115cd7335e6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -3223,6 +3223,7 @@ "<%(count)s spaces>|other": "<%(count)s spaces>", "<%(count)s spaces>|one": "", "<%(count)s spaces>|zero": "", + "See history": "See history", "Send custom state event": "Send custom state event", "Capabilities": "Capabilities", "Failed to load.": "Failed to load.",