Quick and dirty devtool to explore state history (#11197)
* Quick and dirty devtool to explore state history * Include error in unsigned * iterate * Fix silly copy paste
This commit is contained in:
parent
285847560b
commit
706a42f390
4 changed files with 72 additions and 5 deletions
|
@ -31,6 +31,7 @@ export interface IDevtoolsProps {
|
||||||
interface IMinProps extends Pick<IDevtoolsProps, "onBack"> {
|
interface IMinProps extends Pick<IDevtoolsProps, "onBack"> {
|
||||||
className?: string;
|
className?: string;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
extraButton?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IProps extends IMinProps {
|
interface IProps extends IMinProps {
|
||||||
|
@ -38,7 +39,14 @@ interface IProps extends IMinProps {
|
||||||
onAction(): Promise<string | void>;
|
onAction(): Promise<string | void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BaseTool: React.FC<XOR<IMinProps, IProps>> = ({ className, actionLabel, onBack, onAction, children }) => {
|
const BaseTool: React.FC<XOR<IMinProps, IProps>> = ({
|
||||||
|
className,
|
||||||
|
actionLabel,
|
||||||
|
onBack,
|
||||||
|
onAction,
|
||||||
|
children,
|
||||||
|
extraButton,
|
||||||
|
}) => {
|
||||||
const [message, setMessage] = useState<string | null>(null);
|
const [message, setMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
const onBackClick = (): void => {
|
const onBackClick = (): void => {
|
||||||
|
@ -68,6 +76,7 @@ const BaseTool: React.FC<XOR<IMinProps, IProps>> = ({ className, actionLabel, on
|
||||||
<>
|
<>
|
||||||
<div className={classNames("mx_DevTools_content", className)}>{children}</div>
|
<div className={classNames("mx_DevTools_content", className)}>{children}</div>
|
||||||
<div className="mx_Dialog_buttons">
|
<div className="mx_Dialog_buttons">
|
||||||
|
{extraButton}
|
||||||
<button onClick={onBackClick}>{_t("Back")}</button>
|
<button onClick={onBackClick}>{_t("Back")}</button>
|
||||||
{actionButton}
|
{actionButton}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
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 { IContent, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
|
||||||
import { _t, _td } from "../../../../languageHandler";
|
import { _t, _td } from "../../../../languageHandler";
|
||||||
|
@ -143,9 +143,10 @@ export interface IEditorProps extends Pick<IDevtoolsProps, "onBack"> {
|
||||||
|
|
||||||
interface IViewerProps extends Required<IEditorProps> {
|
interface IViewerProps extends Required<IEditorProps> {
|
||||||
Editor: React.FC<IEditorProps>;
|
Editor: React.FC<IEditorProps>;
|
||||||
|
extraButton?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EventViewer: React.FC<IViewerProps> = ({ mxEvent, onBack, Editor }) => {
|
export const EventViewer: React.FC<IViewerProps> = ({ mxEvent, onBack, Editor, extraButton }) => {
|
||||||
const [editing, setEditing] = useState(false);
|
const [editing, setEditing] = useState(false);
|
||||||
|
|
||||||
if (editing) {
|
if (editing) {
|
||||||
|
@ -160,7 +161,7 @@ export const EventViewer: React.FC<IViewerProps> = ({ mxEvent, onBack, Editor })
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseTool onBack={onBack} actionLabel={_t("Edit")} onAction={onAction}>
|
<BaseTool onBack={onBack} actionLabel={_t("Edit")} onAction={onAction} extraButton={extraButton}>
|
||||||
<SyntaxHighlight language="json">{stringify(mxEvent.event)}</SyntaxHighlight>
|
<SyntaxHighlight language="json">{stringify(mxEvent.event)}</SyntaxHighlight>
|
||||||
</BaseTool>
|
</BaseTool>
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,6 +24,9 @@ import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
|
||||||
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
|
||||||
import { EventEditor, EventViewer, eventTypeField, stateKeyField, IEditorProps, stringify } from "./Event";
|
import { EventEditor, EventViewer, eventTypeField, stateKeyField, IEditorProps, stringify } from "./Event";
|
||||||
import FilteredList from "./FilteredList";
|
import FilteredList from "./FilteredList";
|
||||||
|
import Spinner from "../../elements/Spinner";
|
||||||
|
import SyntaxHighlight from "../../elements/SyntaxHighlight";
|
||||||
|
import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
|
||||||
|
|
||||||
export const StateEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack }) => {
|
export const StateEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack }) => {
|
||||||
const context = useContext(DevtoolsContext);
|
const context = useContext(DevtoolsContext);
|
||||||
|
@ -47,6 +50,48 @@ interface StateEventButtonProps {
|
||||||
onClick(): void;
|
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 = <Spinner />;
|
||||||
|
if (events !== null) {
|
||||||
|
body = (
|
||||||
|
<>
|
||||||
|
{events.map((ev) => (
|
||||||
|
<SyntaxHighlight language="json" key={ev.event_id}>
|
||||||
|
{stringify(ev)}
|
||||||
|
</SyntaxHighlight>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <BaseTool onBack={onBack}>{body}</BaseTool>;
|
||||||
|
};
|
||||||
|
|
||||||
const StateEventButton: React.FC<StateEventButtonProps> = ({ label, onClick }) => {
|
const StateEventButton: React.FC<StateEventButtonProps> = ({ label, onClick }) => {
|
||||||
const trimmed = label.trim();
|
const trimmed = label.trim();
|
||||||
|
|
||||||
|
@ -71,6 +116,7 @@ const RoomStateExplorerEventType: React.FC<IEventTypeProps> = ({ eventType, onBa
|
||||||
const context = useContext(DevtoolsContext);
|
const context = useContext(DevtoolsContext);
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState("");
|
||||||
const [event, setEvent] = useState<MatrixEvent | null>(null);
|
const [event, setEvent] = useState<MatrixEvent | null>(null);
|
||||||
|
const [history, setHistory] = useState(false);
|
||||||
|
|
||||||
const events = context.room.currentState.events.get(eventType)!;
|
const events = context.room.currentState.events.get(eventType)!;
|
||||||
|
|
||||||
|
@ -82,6 +128,12 @@ const RoomStateExplorerEventType: React.FC<IEventTypeProps> = ({ eventType, onBa
|
||||||
}
|
}
|
||||||
}, [events]);
|
}, [events]);
|
||||||
|
|
||||||
|
if (event && history) {
|
||||||
|
const _onBack = (): void => {
|
||||||
|
setHistory(false);
|
||||||
|
};
|
||||||
|
return <RoomStateHistory mxEvent={event} onBack={_onBack} />;
|
||||||
|
}
|
||||||
if (event) {
|
if (event) {
|
||||||
const _onBack = (): void => {
|
const _onBack = (): void => {
|
||||||
if (events?.size === 1 && events.has("")) {
|
if (events?.size === 1 && events.has("")) {
|
||||||
|
@ -90,7 +142,11 @@ const RoomStateExplorerEventType: React.FC<IEventTypeProps> = ({ eventType, onBa
|
||||||
setEvent(null);
|
setEvent(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return <EventViewer mxEvent={event} onBack={_onBack} Editor={StateEventEditor} />;
|
const onHistoryClick = (): void => {
|
||||||
|
setHistory(true);
|
||||||
|
};
|
||||||
|
const extraButton = <button onClick={onHistoryClick}>{_t("See history")}</button>;
|
||||||
|
return <EventViewer mxEvent={event} onBack={_onBack} Editor={StateEventEditor} extraButton={extraButton} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -3224,6 +3224,7 @@
|
||||||
"<%(count)s spaces>|other": "<%(count)s spaces>",
|
"<%(count)s spaces>|other": "<%(count)s spaces>",
|
||||||
"<%(count)s spaces>|one": "<space>",
|
"<%(count)s spaces>|one": "<space>",
|
||||||
"<%(count)s spaces>|zero": "<empty string>",
|
"<%(count)s spaces>|zero": "<empty string>",
|
||||||
|
"See history": "See history",
|
||||||
"Send custom state event": "Send custom state event",
|
"Send custom state event": "Send custom state event",
|
||||||
"Capabilities": "Capabilities",
|
"Capabilities": "Capabilities",
|
||||||
"Failed to load.": "Failed to load.",
|
"Failed to load.": "Failed to load.",
|
||||||
|
|
Loading…
Reference in a new issue