import React, { FormEventHandler, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Box, Text, IconButton, Chip, Scroll, config, TextArea, color, Spinner, Button } from 'folds'; import { Icon, Icons } from '../../../components/icons'; import { MatrixError } from 'matrix-js-sdk'; import { Page, PageHeader } from '../../../components/page'; import { SequenceCard } from '../../../components/sequence-card'; import { TextViewerContent } from '../../../components/text-viewer'; import { useStateEvent } from '../../../hooks/useStateEvent'; import { useRoom } from '../../../hooks/useRoom'; import { StateEvent } from '../../../../types/matrix/room'; import { useMatrixClient } from '../../../hooks/useMatrixClient'; import { useAlive } from '../../../hooks/useAlive'; import { Cursor } from '../../../plugins/text-area'; import { AsyncStatus, useAsyncCallback } from '../../../hooks/useAsyncCallback'; import { syntaxErrorPosition } from '../../../utils/dom'; import { SettingTile } from '../../../components/setting-tile'; import { SequenceCardStyle } from '../styles.css'; import { usePowerLevels } from '../../../hooks/usePowerLevels'; import { useTextAreaCodeEditor } from '../../../hooks/useTextAreaCodeEditor'; import { useRoomCreators } from '../../../hooks/useRoomCreators'; import { useRoomPermissions } from '../../../hooks/useRoomPermissions'; const EDITOR_INTENT_SPACE_COUNT = 2; type StateEventEditProps = { type: string; stateKey: string; content: object; requestClose: () => void; }; function StateEventEdit({ type, stateKey, content, requestClose }: StateEventEditProps) { const mx = useMatrixClient(); const room = useRoom(); const alive = useAlive(); const defaultContentStr = useMemo( () => JSON.stringify(content, undefined, EDITOR_INTENT_SPACE_COUNT), [content] ); const textAreaRef = useRef(null); const [jsonError, setJSONError] = useState(); const { handleKeyDown, operations, getTarget } = useTextAreaCodeEditor( textAreaRef, EDITOR_INTENT_SPACE_COUNT ); const [submitState, submit] = useAsyncCallback( useCallback( (c) => mx.sendStateEvent(room.roomId, type as any, c, stateKey), [mx, room, type, stateKey] ) ); const submitting = submitState.status === AsyncStatus.Loading; const handleSubmit: FormEventHandler = (evt) => { evt.preventDefault(); if (submitting) return; const target = evt.target as HTMLFormElement | undefined; const contentTextArea = target?.contentTextArea as HTMLTextAreaElement | undefined; if (!contentTextArea) return; 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 || defaultContentStr === JSON.stringify(parsedContent, null, EDITOR_INTENT_SPACE_COUNT) ) { return; } submit(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 ( State Event } /> {submitState.status === AsyncStatus.Error && ( {submitState.error.message} )} JSON Content