import React, { useCallback, useRef, useState, FormEventHandler, useEffect } from 'react'; import { MatrixError } from 'matrix-js-sdk'; import { Box, Chip, Icon, Icons, IconButton, Text, config, Button, Spinner, color, TextArea as TextAreaComponent, Input, } from 'folds'; import { Page, PageHeader } from '../../../components/page'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useRoom } from '../../../hooks/useRoom'; import { useAlive } from '../../../hooks/useAlive'; import { useTextAreaCodeEditor } from '../../../hooks/useTextAreaCodeEditor'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { syntaxErrorPosition } from '../../../utils/dom'; import { Cursor } from '../../../plugins/text-area'; const EDITOR_INTENT_SPACE_COUNT = 2; export type SendRoomEventProps = { type?: string; stateKey?: string; requestClose: () => void; }; export function SendRoomEvent({ type, stateKey, requestClose }: SendRoomEventProps) { const mx = useMatrixClient(); const room = useRoom(); const alive = useAlive(); const composeStateEvent = typeof stateKey === 'string'; const textAreaRef = useRef(null); const [jsonError, setJSONError] = useState(); const { handleKeyDown, operations, getTarget } = useTextAreaCodeEditor( textAreaRef, EDITOR_INTENT_SPACE_COUNT ); const [submitState, submit] = useAsyncCallback< object, MatrixError, [string, string | undefined, object] >( useCallback( (evtType, evtStateKey, evtContent) => { if (typeof evtStateKey === 'string') { return mx.sendStateEvent(room.roomId, evtType as any, evtContent, evtStateKey); } return mx.sendEvent(room.roomId, evtType as any, evtContent); }, [mx, room] ) ); const submitting = submitState.status === AsyncStatus.Loading; const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (submitting) return; const target = evt.target as HTMLFormElement | undefined; const typeInput = target?.typeInput as HTMLInputElement | undefined; const stateKeyInput = target?.stateKeyInput as HTMLInputElement | undefined; const contentTextArea = target?.contentTextArea as HTMLTextAreaElement | undefined; if (!typeInput || !contentTextArea) return; const evtType = typeInput.value; const evtStateKey = stateKeyInput?.value; const contentStr = contentTextArea.value.trim(); let parsedContent: object; try { parsedContent = JSON.parse(contentStr); } catch (e) { setJSONError(e as SyntaxError); return; } setJSONError(undefined); if (parsedContent === null) { return; } submit(evtType, evtStateKey, parsedContent).then(() => { if (alive()) { requestClose(); } }); }; useEffect(() => { if (jsonError) { const errorPosition = syntaxErrorPosition(jsonError) ?? 0; const cursor = new Cursor(errorPosition, errorPosition, 'none'); operations.select(cursor); getTarget()?.focus(); } }, [jsonError, operations, getTarget]); return ( } > Developer Tools {composeStateEvent ? 'State Event Type' : 'Message Event Type'} {submitState.status === AsyncStatus.Error && ( {submitState.error.message} )} {composeStateEvent && ( State Key (Optional) )} JSON Content {jsonError && ( {jsonError.name}: {jsonError.message} )} ); }