UNPKG

matrix-react-sdk

Version:
1,251 lines (1,098 loc) 154 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.SendCustomEvent = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var sdk = _interopRequireWildcard(require("../../../index")); var _SyntaxHighlight = _interopRequireDefault(require("../elements/SyntaxHighlight")); var _languageHandler = require("../../../languageHandler"); var _Field = _interopRequireDefault(require("../elements/Field")); var _MatrixClientContext = _interopRequireDefault(require("../../../contexts/MatrixClientContext")); var _useEventEmitter = require("../../../hooks/useEventEmitter"); var _VerificationRequest = require("matrix-js-sdk/src/crypto/verification/request/VerificationRequest"); var _WidgetStore = _interopRequireDefault(require("../../../stores/WidgetStore")); var _AsyncStore = require("../../../stores/AsyncStore"); var _Settings = require("../../../settings/Settings"); var _SettingsStore = _interopRequireWildcard(require("../../../settings/SettingsStore")); var _Modal = _interopRequireDefault(require("../../../Modal")); var _ErrorDialog = _interopRequireDefault(require("./ErrorDialog")); var _replaceableComponent = require("../../../utils/replaceableComponent"); var _room = require("matrix-js-sdk/src/models/room"); var _event = require("matrix-js-sdk/src/models/event"); var _dec, _class, _class2, _temp; class GenericEditor extends _react.default.PureComponent { // static propTypes = {onBack: PropTypes.func.isRequired}; constructor(props) { super(props); this._onChange = this._onChange.bind(this); this.onBack = this.onBack.bind(this); } onBack() { if (this.state.message) { this.setState({ message: null }); } else { this.props.onBack(); } } _onChange(e) { this.setState({ [e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value }); } _buttons() { return /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), !this.state.message && /*#__PURE__*/_react.default.createElement("button", { onClick: this._send }, (0, _languageHandler._t)('Send'))); } textInput(id, label) { return /*#__PURE__*/_react.default.createElement(_Field.default, { id: id, label: label, size: "42", autoFocus: true, type: "text", autoComplete: "on", value: this.state[id], onChange: this._onChange }); } } class SendCustomEvent extends GenericEditor { static getLabel() { return (0, _languageHandler._t)('Send Custom Event'); } constructor(props) { super(props); this._send = this._send.bind(this); const { eventType, stateKey, evContent } = Object.assign({ eventType: '', stateKey: '', evContent: '{\n\n}' }, this.props.inputs); this.state = { isStateEvent: Boolean(this.props.forceStateEvent), eventType, stateKey, evContent }; } send(content) { const cli = this.context; if (this.state.isStateEvent) { return cli.sendStateEvent(this.props.room.roomId, this.state.eventType, content, this.state.stateKey); } else { return cli.sendEvent(this.props.room.roomId, this.state.eventType, content); } } async _send() { if (this.state.eventType === '') { this.setState({ message: (0, _languageHandler._t)('You must specify an event type!') }); return; } let message; try { const content = JSON.parse(this.state.evContent); await this.send(content); message = (0, _languageHandler._t)('Event sent!'); } catch (e) { message = (0, _languageHandler._t)('Failed to send custom event.') + ' (' + e.toString() + ')'; } this.setState({ message }); } render() { if (this.state.message) { return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, this.state.message), this._buttons()); } const showTglFlip = !this.state.message && !this.props.forceStateEvent && !this.props.forceGeneralEvent; return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_content" }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_eventTypeStateKeyGroup" }, this.textInput('eventType', (0, _languageHandler._t)('Event Type')), this.state.isStateEvent && this.textInput('stateKey', (0, _languageHandler._t)('State Key'))), /*#__PURE__*/_react.default.createElement("br", null), /*#__PURE__*/_react.default.createElement(_Field.default, { id: "evContent", label: (0, _languageHandler._t)("Event Content"), type: "text", className: "mx_DevTools_textarea", autoComplete: "off", value: this.state.evContent, onChange: this._onChange, element: "textarea" })), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), !this.state.message && /*#__PURE__*/_react.default.createElement("button", { onClick: this._send }, (0, _languageHandler._t)('Send')), showTglFlip && /*#__PURE__*/_react.default.createElement("div", { style: { float: "right" } }, /*#__PURE__*/_react.default.createElement("input", { id: "isStateEvent", className: "mx_DevTools_tgl mx_DevTools_tgl-flip", type: "checkbox", onChange: this._onChange, checked: this.state.isStateEvent }), /*#__PURE__*/_react.default.createElement("label", { className: "mx_DevTools_tgl-btn", "data-tg-off": "Event", "data-tg-on": "State Event", htmlFor: "isStateEvent" })))); } } exports.SendCustomEvent = SendCustomEvent; (0, _defineProperty2.default)(SendCustomEvent, "propTypes", { onBack: _propTypes.default.func.isRequired, room: _propTypes.default.instanceOf(_room.Room).isRequired, forceStateEvent: _propTypes.default.bool, forceGeneralEvent: _propTypes.default.bool, inputs: _propTypes.default.object }); (0, _defineProperty2.default)(SendCustomEvent, "contextType", _MatrixClientContext.default); class SendAccountData extends GenericEditor { static getLabel() { return (0, _languageHandler._t)('Send Account Data'); } constructor(props) { super(props); this._send = this._send.bind(this); const { eventType, evContent } = Object.assign({ eventType: '', evContent: '{\n\n}' }, this.props.inputs); this.state = { isRoomAccountData: Boolean(this.props.isRoomAccountData), eventType, evContent }; } send(content) { const cli = this.context; if (this.state.isRoomAccountData) { return cli.setRoomAccountData(this.props.room.roomId, this.state.eventType, content); } return cli.setAccountData(this.state.eventType, content); } async _send() { if (this.state.eventType === '') { this.setState({ message: (0, _languageHandler._t)('You must specify an event type!') }); return; } let message; try { const content = JSON.parse(this.state.evContent); await this.send(content); message = (0, _languageHandler._t)('Event sent!'); } catch (e) { message = (0, _languageHandler._t)('Failed to send custom event.') + ' (' + e.toString() + ')'; } this.setState({ message }); } render() { if (this.state.message) { return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, this.state.message), this._buttons()); } return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_content" }, this.textInput('eventType', (0, _languageHandler._t)('Event Type')), /*#__PURE__*/_react.default.createElement("br", null), /*#__PURE__*/_react.default.createElement(_Field.default, { id: "evContent", label: (0, _languageHandler._t)("Event Content"), type: "text", className: "mx_DevTools_textarea", autoComplete: "off", value: this.state.evContent, onChange: this._onChange, element: "textarea" })), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), !this.state.message && /*#__PURE__*/_react.default.createElement("button", { onClick: this._send }, (0, _languageHandler._t)('Send')), !this.state.message && /*#__PURE__*/_react.default.createElement("div", { style: { float: "right" } }, /*#__PURE__*/_react.default.createElement("input", { id: "isRoomAccountData", className: "mx_DevTools_tgl mx_DevTools_tgl-flip", type: "checkbox", onChange: this._onChange, checked: this.state.isRoomAccountData, disabled: this.props.forceMode }), /*#__PURE__*/_react.default.createElement("label", { className: "mx_DevTools_tgl-btn", "data-tg-off": "Account Data", "data-tg-on": "Room Data", htmlFor: "isRoomAccountData" })))); } } (0, _defineProperty2.default)(SendAccountData, "propTypes", { room: _propTypes.default.instanceOf(_room.Room).isRequired, isRoomAccountData: _propTypes.default.bool, forceMode: _propTypes.default.bool, inputs: _propTypes.default.object }); (0, _defineProperty2.default)(SendAccountData, "contextType", _MatrixClientContext.default); const INITIAL_LOAD_TILES = 20; const LOAD_TILES_STEP_SIZE = 50; class FilteredList extends _react.default.PureComponent { static filterChildren(children, query) { if (!query) return children; const lcQuery = query.toLowerCase(); return children.filter(child => child.key.toLowerCase().includes(lcQuery)); } constructor(props) { super(props); (0, _defineProperty2.default)(this, "showAll", () => { this.setState({ truncateAt: this.state.truncateAt + LOAD_TILES_STEP_SIZE }); }); (0, _defineProperty2.default)(this, "createOverflowElement", (overflowCount /*: number*/ , totalCount /*: number*/ ) => { return /*#__PURE__*/_react.default.createElement("button", { className: "mx_DevTools_RoomStateExplorer_button", onClick: this.showAll }, (0, _languageHandler._t)("and %(count)s others...", { count: overflowCount })); }); (0, _defineProperty2.default)(this, "onQuery", ev => { if (this.props.onChange) this.props.onChange(ev.target.value); }); (0, _defineProperty2.default)(this, "getChildren", (start /*: number*/ , end /*: number*/ ) => { return this.state.filteredChildren.slice(start, end); }); (0, _defineProperty2.default)(this, "getChildCount", () => /*: number*/ { return this.state.filteredChildren.length; }); this.state = { filteredChildren: FilteredList.filterChildren(this.props.children, this.props.query), truncateAt: INITIAL_LOAD_TILES }; } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event UNSAFE_componentWillReceiveProps(nextProps) { // eslint-disable-line camelcase if (this.props.children === nextProps.children && this.props.query === nextProps.query) return; this.setState({ filteredChildren: FilteredList.filterChildren(nextProps.children, nextProps.query), truncateAt: INITIAL_LOAD_TILES }); } render() { const TruncatedList = sdk.getComponent("elements.TruncatedList"); return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Field.default, { label: (0, _languageHandler._t)('Filter results'), autoFocus: true, size: 64, type: "text", autoComplete: "off", value: this.props.query, onChange: this.onQuery, className: "mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" // force re-render so that autoFocus is applied when this component is re-used , key: this.props.children[0] ? this.props.children[0].key : '' }), /*#__PURE__*/_react.default.createElement(TruncatedList, { getChildren: this.getChildren, getChildCount: this.getChildCount, truncateAt: this.state.truncateAt, createOverflowElement: this.createOverflowElement })); } } (0, _defineProperty2.default)(FilteredList, "propTypes", { children: _propTypes.default.any, query: _propTypes.default.string, onChange: _propTypes.default.func }); class RoomStateExplorer extends _react.default.PureComponent { static getLabel() { return (0, _languageHandler._t)('Explore Room State'); } constructor(props) { super(props); (0, _defineProperty2.default)(this, "roomStateEvents", void 0); this.roomStateEvents = this.props.room.currentState.events; this.onBack = this.onBack.bind(this); this.editEv = this.editEv.bind(this); this.onQueryEventType = this.onQueryEventType.bind(this); this.onQueryStateKey = this.onQueryStateKey.bind(this); this.state = { eventType: null, event: null, editing: false, queryEventType: '', queryStateKey: '' }; } browseEventType(eventType) { return () => { this.setState({ eventType }); }; } onViewSourceClick(event) { return () => { this.setState({ event }); }; } onBack() { if (this.state.editing) { this.setState({ editing: false }); } else if (this.state.event) { this.setState({ event: null }); } else if (this.state.eventType) { this.setState({ eventType: null }); } else { this.props.onBack(); } } editEv() { this.setState({ editing: true }); } onQueryEventType(filterEventType) { this.setState({ queryEventType: filterEventType }); } onQueryStateKey(filterStateKey) { this.setState({ queryStateKey: filterStateKey }); } render() { if (this.state.event) { if (this.state.editing) { return /*#__PURE__*/_react.default.createElement(SendCustomEvent, { room: this.props.room, forceStateEvent: true, onBack: this.onBack, inputs: { eventType: this.state.event.getType(), evContent: JSON.stringify(this.state.event.getContent(), null, '\t'), stateKey: this.state.event.getStateKey() } }); } return /*#__PURE__*/_react.default.createElement("div", { className: "mx_ViewSource" }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, /*#__PURE__*/_react.default.createElement(_SyntaxHighlight.default, { className: "json" }, JSON.stringify(this.state.event.event, null, 2))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), /*#__PURE__*/_react.default.createElement("button", { onClick: this.editEv }, (0, _languageHandler._t)('Edit')))); } let list = null; const classes = 'mx_DevTools_RoomStateExplorer_button'; if (this.state.eventType === null) { list = /*#__PURE__*/_react.default.createElement(FilteredList, { query: this.state.queryEventType, onChange: this.onQueryEventType }, Array.from(this.roomStateEvents.entries()).map(([eventType, allStateKeys]) => { let onClickFn; if (allStateKeys.size === 1 && allStateKeys.has("")) { onClickFn = this.onViewSourceClick(allStateKeys.get("")); } else { onClickFn = this.browseEventType(eventType); } return /*#__PURE__*/_react.default.createElement("button", { className: classes, key: eventType, onClick: onClickFn }, eventType); })); } else { const stateGroup = this.roomStateEvents.get(this.state.eventType); list = /*#__PURE__*/_react.default.createElement(FilteredList, { query: this.state.queryStateKey, onChange: this.onQueryStateKey }, Array.from(stateGroup.entries()).map(([stateKey, ev]) => { return /*#__PURE__*/_react.default.createElement("button", { className: classes, key: stateKey, onClick: this.onViewSourceClick(ev) }, stateKey); })); } return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, list), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')))); } } (0, _defineProperty2.default)(RoomStateExplorer, "propTypes", { onBack: _propTypes.default.func.isRequired, room: _propTypes.default.instanceOf(_room.Room).isRequired }); (0, _defineProperty2.default)(RoomStateExplorer, "contextType", _MatrixClientContext.default); class AccountDataExplorer extends _react.default.PureComponent { static getLabel() { return (0, _languageHandler._t)('Explore Account Data'); } constructor(props) { super(props); this.onBack = this.onBack.bind(this); this.editEv = this.editEv.bind(this); this._onChange = this._onChange.bind(this); this.onQueryEventType = this.onQueryEventType.bind(this); this.state = { isRoomAccountData: false, event: null, editing: false, queryEventType: '' }; } getData() { if (this.state.isRoomAccountData) { return this.props.room.accountData; } return this.context.store.accountData; } onViewSourceClick(event) { return () => { this.setState({ event }); }; } onBack() { if (this.state.editing) { this.setState({ editing: false }); } else if (this.state.event) { this.setState({ event: null }); } else { this.props.onBack(); } } _onChange(e) { this.setState({ [e.target.id]: e.target.type === 'checkbox' ? e.target.checked : e.target.value }); } editEv() { this.setState({ editing: true }); } onQueryEventType(queryEventType) { this.setState({ queryEventType }); } render() { if (this.state.event) { if (this.state.editing) { return /*#__PURE__*/_react.default.createElement(SendAccountData, { room: this.props.room, isRoomAccountData: this.state.isRoomAccountData, onBack: this.onBack, inputs: { eventType: this.state.event.getType(), evContent: JSON.stringify(this.state.event.getContent(), null, '\t') }, forceMode: true }); } return /*#__PURE__*/_react.default.createElement("div", { className: "mx_ViewSource" }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_content" }, /*#__PURE__*/_react.default.createElement(_SyntaxHighlight.default, { className: "json" }, JSON.stringify(this.state.event.event, null, 2))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), /*#__PURE__*/_react.default.createElement("button", { onClick: this.editEv }, (0, _languageHandler._t)('Edit')))); } const rows = []; const classes = 'mx_DevTools_RoomStateExplorer_button'; const data = this.getData(); Object.keys(data).forEach(evType => { const ev = data[evType]; rows.push( /*#__PURE__*/_react.default.createElement("button", { className: classes, key: evType, onClick: this.onViewSourceClick(ev) }, evType)); }); return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, /*#__PURE__*/_react.default.createElement(FilteredList, { query: this.state.queryEventType, onChange: this.onQueryEventType }, rows)), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)('Back')), !this.state.message && /*#__PURE__*/_react.default.createElement("div", { style: { float: "right" } }, /*#__PURE__*/_react.default.createElement("input", { id: "isRoomAccountData", className: "mx_DevTools_tgl mx_DevTools_tgl-flip", type: "checkbox", onChange: this._onChange, checked: this.state.isRoomAccountData }), /*#__PURE__*/_react.default.createElement("label", { className: "mx_DevTools_tgl-btn", "data-tg-off": "Account Data", "data-tg-on": "Room Data", htmlFor: "isRoomAccountData" })))); } } (0, _defineProperty2.default)(AccountDataExplorer, "propTypes", { onBack: _propTypes.default.func.isRequired, room: _propTypes.default.instanceOf(_room.Room).isRequired }); (0, _defineProperty2.default)(AccountDataExplorer, "contextType", _MatrixClientContext.default); class ServersInRoomList extends _react.default.PureComponent { static getLabel() { return (0, _languageHandler._t)('View Servers in Room'); } constructor(props) { super(props); (0, _defineProperty2.default)(this, "onQuery", query => { this.setState({ query }); }); const room = this.props.room; const servers = new Set(); room.currentState.getStateEvents("m.room.member").forEach(ev => servers.add(ev.getSender().split(":")[1])); this.servers = Array.from(servers).map(s => /*#__PURE__*/_react.default.createElement("button", { key: s, className: "mx_DevTools_ServersInRoomList_button" }, s)); this.state = { query: '' }; } render() { return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, /*#__PURE__*/_react.default.createElement(FilteredList, { query: this.state.query, onChange: this.onQuery }, this.servers)), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.props.onBack }, (0, _languageHandler._t)('Back')))); } } (0, _defineProperty2.default)(ServersInRoomList, "propTypes", { onBack: _propTypes.default.func.isRequired, room: _propTypes.default.instanceOf(_room.Room).isRequired }); (0, _defineProperty2.default)(ServersInRoomList, "contextType", _MatrixClientContext.default); const PHASE_MAP = { [_VerificationRequest.PHASE_UNSENT]: "unsent", [_VerificationRequest.PHASE_REQUESTED]: "requested", [_VerificationRequest.PHASE_READY]: "ready", [_VerificationRequest.PHASE_DONE]: "done", [_VerificationRequest.PHASE_STARTED]: "started", [_VerificationRequest.PHASE_CANCELLED]: "cancelled" }; function VerificationRequest({ txnId, request }) { const [, updateState] = (0, _react.useState)(); const [timeout, setRequestTimeout] = (0, _react.useState)(request.timeout); /* Re-render if something changes state */ (0, _useEventEmitter.useEventEmitter)(request, "change", updateState); /* Keep re-rendering if there's a timeout */ (0, _react.useEffect)(() => { if (request.timeout == 0) return; /* Note that request.timeout is a getter, so its value changes */ const id = setInterval(() => { setRequestTimeout(request.timeout); }, 500); return () => { clearInterval(id); }; }, [request]); return /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_VerificationRequest" }, /*#__PURE__*/_react.default.createElement("dl", null, /*#__PURE__*/_react.default.createElement("dt", null, "Transaction"), /*#__PURE__*/_react.default.createElement("dd", null, txnId), /*#__PURE__*/_react.default.createElement("dt", null, "Phase"), /*#__PURE__*/_react.default.createElement("dd", null, PHASE_MAP[request.phase] || request.phase), /*#__PURE__*/_react.default.createElement("dt", null, "Timeout"), /*#__PURE__*/_react.default.createElement("dd", null, Math.floor(timeout / 1000)), /*#__PURE__*/_react.default.createElement("dt", null, "Methods"), /*#__PURE__*/_react.default.createElement("dd", null, request.methods && request.methods.join(", ")), /*#__PURE__*/_react.default.createElement("dt", null, "requestingUserId"), /*#__PURE__*/_react.default.createElement("dd", null, request.requestingUserId), /*#__PURE__*/_react.default.createElement("dt", null, "observeOnly"), /*#__PURE__*/_react.default.createElement("dd", null, JSON.stringify(request.observeOnly)))); } class VerificationExplorer extends _react.default.Component { constructor(...args) { super(...args); (0, _defineProperty2.default)(this, "onNewRequest", () => { this.forceUpdate(); }); } static getLabel() { return (0, _languageHandler._t)("Verification Requests"); } /* Ensure this.context is the cli */ componentDidMount() { const cli = this.context; cli.on("crypto.verification.request", this.onNewRequest); } componentWillUnmount() { const cli = this.context; cli.off("crypto.verification.request", this.onNewRequest); } render() { const cli = this.context; const room = this.props.room; const inRoomChannel = cli._crypto._inRoomVerificationRequests; const inRoomRequests = (inRoomChannel._requestsByRoomId || new Map()).get(room.roomId) || new Map(); return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, Array.from(inRoomRequests.entries()).reverse().map(([txnId, request]) => /*#__PURE__*/_react.default.createElement(VerificationRequest, { txnId: txnId, request: request, key: txnId }))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.props.onBack }, (0, _languageHandler._t)("Back")))); } } (0, _defineProperty2.default)(VerificationExplorer, "contextType", _MatrixClientContext.default); class WidgetExplorer extends _react.default.Component { static getLabel() { return (0, _languageHandler._t)("Active Widgets"); } constructor(props) { super(props); (0, _defineProperty2.default)(this, "onWidgetStoreUpdate", () => { this.forceUpdate(); }); (0, _defineProperty2.default)(this, "onQueryChange", query => { this.setState({ query }); }); (0, _defineProperty2.default)(this, "onEditWidget", widget => { this.setState({ editWidget: widget }); }); (0, _defineProperty2.default)(this, "onBack", () => { const widgets = _WidgetStore.default.instance.getApps(this.props.room.roomId); if (this.state.editWidget && widgets.includes(this.state.editWidget)) { this.setState({ editWidget: null }); } else { this.props.onBack(); } }); this.state = { query: '', editWidget: null // set to an IApp when editing }; } componentDidMount() { _WidgetStore.default.instance.on(_AsyncStore.UPDATE_EVENT, this.onWidgetStoreUpdate); } componentWillUnmount() { _WidgetStore.default.instance.off(_AsyncStore.UPDATE_EVENT, this.onWidgetStoreUpdate); } render() { const room = this.props.room; const editWidget = this.state.editWidget; const widgets = _WidgetStore.default.instance.getApps(room.roomId); if (editWidget && widgets.includes(editWidget)) { const allState = Array.from(Array.from(room.currentState.events.values()).map(e => e.values())).reduce((p, c) => { p.push(...c); return p; }, []); const stateEv = allState.find(ev => ev.getId() === editWidget.eventId); if (!stateEv) { // "should never happen" return /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("There was an error finding this widget."), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)("Back")))); } return /*#__PURE__*/_react.default.createElement(SendCustomEvent, { onBack: this.onBack, room: room, forceStateEvent: true, inputs: { eventType: stateEv.getType(), evContent: JSON.stringify(stateEv.getContent(), null, '\t'), stateKey: stateEv.getStateKey() } }); } return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, /*#__PURE__*/_react.default.createElement(FilteredList, { query: this.state.query, onChange: this.onQueryChange }, widgets.map(w => { return /*#__PURE__*/_react.default.createElement("button", { className: "mx_DevTools_RoomStateExplorer_button", key: w.url + w.eventId, onClick: () => this.onEditWidget(w) }, w.url); }))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)("Back")))); } } class SettingsExplorer extends _react.default.Component { static getLabel() { return (0, _languageHandler._t)("Settings Explorer"); } constructor(props) { super(props); (0, _defineProperty2.default)(this, "onQueryChange", ev => { this.setState({ query: ev.target.value }); }); (0, _defineProperty2.default)(this, "onExplValuesEdit", ev => { this.setState({ explicitValues: ev.target.value }); }); (0, _defineProperty2.default)(this, "onExplRoomValuesEdit", ev => { this.setState({ explicitRoomValues: ev.target.value }); }); (0, _defineProperty2.default)(this, "onBack", () => { if (this.state.editSetting) { this.setState({ editSetting: null }); } else if (this.state.viewSetting) { this.setState({ viewSetting: null }); } else { this.props.onBack(); } }); (0, _defineProperty2.default)(this, "onViewClick", (ev, settingId) => { ev.preventDefault(); this.setState({ viewSetting: settingId }); }); (0, _defineProperty2.default)(this, "onEditClick", (ev, settingId) => { ev.preventDefault(); this.setState({ editSetting: settingId, explicitValues: this.renderExplicitSettingValues(settingId, null), explicitRoomValues: this.renderExplicitSettingValues(settingId, this.props.room.roomId) }); }); (0, _defineProperty2.default)(this, "onSaveClick", async () => { try { const settingId = this.state.editSetting; const parsedExplicit = JSON.parse(this.state.explicitValues); const parsedExplicitRoom = JSON.parse(this.state.explicitRoomValues); for (const level of Object.keys(parsedExplicit)) { console.log(`[Devtools] Setting value of ${settingId} at ${level} from user input`); try { const val = parsedExplicit[level]; await _SettingsStore.default.setValue(settingId, null, level, val); } catch (e) { console.warn(e); } } const roomId = this.props.room.roomId; for (const level of Object.keys(parsedExplicit)) { console.log(`[Devtools] Setting value of ${settingId} at ${level} in ${roomId} from user input`); try { const val = parsedExplicitRoom[level]; await _SettingsStore.default.setValue(settingId, roomId, level, val); } catch (e) { console.warn(e); } } this.setState({ viewSetting: settingId, editSetting: null }); } catch (e) { _Modal.default.createTrackedDialog('Devtools - Failed to save settings', '', _ErrorDialog.default, { title: (0, _languageHandler._t)("Failed to save settings"), description: e.message }); } }); this.state = { query: '', editSetting: null, // set to a setting ID when editing viewSetting: null, // set to a setting ID when exploring in detail explicitValues: null, // stringified JSON for edit view explicitRoomValues: null // stringified JSON for edit view }; } renderSettingValue(val) { // Note: we don't .toString() a string because we want JSON.stringify to inject quotes for us const toStringTypes = ['boolean', 'number']; if (toStringTypes.includes(typeof val)) { return val.toString(); } else { return JSON.stringify(val); } } renderExplicitSettingValues(setting, roomId) { const vals = {}; for (const level of _SettingsStore.LEVEL_ORDER) { try { vals[level] = _SettingsStore.default.getValueAt(level, setting, roomId, true, true); if (vals[level] === undefined) { vals[level] = null; } } catch (e) { console.warn(e); } } return JSON.stringify(vals, null, 4); } renderCanEditLevel(roomId, level) { const canEdit = _SettingsStore.default.canSetValue(this.state.editSetting, roomId, level); const className = canEdit ? 'mx_DevTools_SettingsExplorer_mutable' : 'mx_DevTools_SettingsExplorer_immutable'; return /*#__PURE__*/_react.default.createElement("td", { className: className }, /*#__PURE__*/_react.default.createElement("code", null, canEdit.toString())); } render() { const room = this.props.room; if (!this.state.viewSetting && !this.state.editSetting) { // view all settings const allSettings = Object.keys(_Settings.SETTINGS).filter(n => this.state.query ? n.toLowerCase().includes(this.state.query.toLowerCase()) : true); return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content mx_DevTools_SettingsExplorer" }, /*#__PURE__*/_react.default.createElement(_Field.default, { label: (0, _languageHandler._t)('Filter results'), autoFocus: true, size: 64, type: "text", autoComplete: "off", value: this.state.query, onChange: this.onQueryChange, className: "mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" }), /*#__PURE__*/_react.default.createElement("table", null, /*#__PURE__*/_react.default.createElement("thead", null, /*#__PURE__*/_react.default.createElement("tr", null, /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Setting ID")), /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Value")), /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Value in this room")))), /*#__PURE__*/_react.default.createElement("tbody", null, allSettings.map(i => /*#__PURE__*/_react.default.createElement("tr", { key: i }, /*#__PURE__*/_react.default.createElement("td", null, /*#__PURE__*/_react.default.createElement("a", { href: "", onClick: e => this.onViewClick(e, i) }, /*#__PURE__*/_react.default.createElement("code", null, i)), /*#__PURE__*/_react.default.createElement("a", { href: "", onClick: e => this.onEditClick(e, i), className: "mx_DevTools_SettingsExplorer_edit" }, "\u270F")), /*#__PURE__*/_react.default.createElement("td", null, /*#__PURE__*/_react.default.createElement("code", null, this.renderSettingValue(_SettingsStore.default.getValue(i)))), /*#__PURE__*/_react.default.createElement("td", null, /*#__PURE__*/_react.default.createElement("code", null, this.renderSettingValue(_SettingsStore.default.getValue(i, room.roomId))))))))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)("Back")))); } else if (this.state.editSetting) { return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content mx_DevTools_SettingsExplorer" }, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("Setting:"), " ", /*#__PURE__*/_react.default.createElement("code", null, this.state.editSetting)), /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_SettingsExplorer_warning" }, /*#__PURE__*/_react.default.createElement("b", null, (0, _languageHandler._t)("Caution:")), " ", (0, _languageHandler._t)("This UI does NOT check the types of the values. Use at your own risk.")), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Setting definition:"), /*#__PURE__*/_react.default.createElement("pre", null, /*#__PURE__*/_react.default.createElement("code", null, JSON.stringify(_Settings.SETTINGS[this.state.editSetting], null, 4)))), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("table", null, /*#__PURE__*/_react.default.createElement("thead", null, /*#__PURE__*/_react.default.createElement("tr", null, /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Level")), /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Settable at global")), /*#__PURE__*/_react.default.createElement("th", null, (0, _languageHandler._t)("Settable at room")))), /*#__PURE__*/_react.default.createElement("tbody", null, _SettingsStore.LEVEL_ORDER.map(lvl => /*#__PURE__*/_react.default.createElement("tr", { key: lvl }, /*#__PURE__*/_react.default.createElement("td", null, /*#__PURE__*/_react.default.createElement("code", null, lvl)), this.renderCanEditLevel(null, lvl), this.renderCanEditLevel(room.roomId, lvl)))))), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Field.default, { id: "valExpl", label: (0, _languageHandler._t)("Values at explicit levels"), type: "text", className: "mx_DevTools_textarea", element: "textarea", autoComplete: "off", value: this.state.explicitValues, onChange: this.onExplValuesEdit })), /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Field.default, { id: "valExpl", label: (0, _languageHandler._t)("Values at explicit levels in this room"), type: "text", className: "mx_DevTools_textarea", element: "textarea", autoComplete: "off", value: this.state.explicitRoomValues, onChange: this.onExplRoomValuesEdit }))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onSaveClick }, (0, _languageHandler._t)("Save setting values")), /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)("Back")))); } else if (this.state.viewSetting) { return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content mx_DevTools_SettingsExplorer" }, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("Setting:"), " ", /*#__PURE__*/_react.default.createElement("code", null, this.state.viewSetting)), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Setting definition:"), /*#__PURE__*/_react.default.createElement("pre", null, /*#__PURE__*/_react.default.createElement("code", null, JSON.stringify(_Settings.SETTINGS[this.state.viewSetting], null, 4)))), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Value:"), "\xA0", /*#__PURE__*/_react.default.createElement("code", null, this.renderSettingValue(_SettingsStore.default.getValue(this.state.viewSetting)))), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Value in this room:"), "\xA0", /*#__PURE__*/_react.default.createElement("code", null, this.renderSettingValue(_SettingsStore.default.getValue(this.state.viewSetting, room.roomId)))), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Values at explicit levels:"), /*#__PURE__*/_react.default.createElement("pre", null, /*#__PURE__*/_react.default.createElement("code", null, this.renderExplicitSettingValues(this.state.viewSetting, null)))), /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("Values at explicit levels in this room:"), /*#__PURE__*/_react.default.createElement("pre", null, /*#__PURE__*/_react.default.createElement("code", null, this.renderExplicitSettingValues(this.state.viewSetting, room.roomId))))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: e => this.onEditClick(e, this.state.viewSetting) }, (0, _languageHandler._t)("Edit Values")), /*#__PURE__*/_react.default.createElement("button", { onClick: this.onBack }, (0, _languageHandler._t)("Back")))); } } } const Entries = [SendCustomEvent, RoomStateExplorer, SendAccountData, AccountDataExplorer, ServersInRoomList, VerificationExplorer, WidgetExplorer, SettingsExplorer]; let DevtoolsDialog = (_dec = (0, _replaceableComponent.replaceableComponent)("views.dialogs.DevtoolsDialog"), _dec(_class = (_temp = _class2 = class DevtoolsDialog extends _react.default.PureComponent { constructor(props) { super(props); this.onBack = this.onBack.bind(this); this.onCancel = this.onCancel.bind(this); this.state = { mode: null }; } componentWillUnmount() { this._unmounted = true; } _setMode(mode) { return () => { this.setState({ mode }); }; } onBack() { if (this.prevMode) { this.setState({ mode: this.prevMode }); this.prevMode = null; } else { this.setState({ mode: null }); } } onCancel() { this.props.onFinished(false); } render() { let body; if (this.state.mode) { body = /*#__PURE__*/_react.default.createElement(_MatrixClientContext.default.Consumer, null, cli => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_left" }, this.state.mode.getLabel()), /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_right" }, "Room ID: ", this.props.roomId), /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_bottom" }), /*#__PURE__*/_react.default.createElement(this.state.mode, { onBack: this.onBack, room: cli.getRoom(this.props.roomId) }))); } else { const classes = "mx_DevTools_RoomStateExplorer_button"; body = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_left" }, (0, _languageHandler._t)('Toolbox')), /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_right" }, "Room ID: ", this.props.roomId), /*#__PURE__*/_react.default.createElement("div", { className: "mx_DevTools_label_bottom" }), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, Entries.map(Entry => { const label = Entry.getLabel(); const onClick = this._setMode(Entry); return /*#__PURE__*/_react.default.createElement("button", { className: classes, key: label, onClick: onClick }, label); }))), /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_buttons" }, /*#__PURE__*/_react.default.createElement("button", { onClick: this.onCancel }, (0, _languageHandler._t)('Cancel')))); } const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); return /*#__PURE__*/_react.default.createElement(BaseDialog, { className: "mx_QuestionDialog", onFinished: this.props.onFinished, title: (0, _languageHandler._t)('Developer Tools') }, body); } }, (0, _defineProperty2.default)(_class2, "propTypes", { roomId: _propTypes.default.string.isRequired, onFinished: _propTypes.default.func.isRequired }), _temp)) || _class); exports.default = DevtoolsDialog; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL2RpYWxvZ3MvRGV2dG9vbHNEaWFsb2cuanMiXSwibmFtZXMiOlsiR2VuZXJpY0VkaXRvciIsIlJlYWN0IiwiUHVyZUNvbXBvbmVudCIsImNvbnN0cnVjdG9yIiwicHJvcHMiLCJfb25DaGFuZ2UiLCJiaW5kIiwib25CYWNrIiwic3RhdGUiLCJtZXNzYWdlIiwic2V0U3RhdGUiLCJlIiwidGFyZ2V0IiwiaWQiLCJ0eXBlIiwiY2hlY2tlZCIsInZhbHVlIiwiX2J1dHRvbnMiLCJfc2VuZCIsInRleHRJbnB1dCIsImxhYmVsIiwiU2VuZEN1c3RvbUV2ZW50IiwiZ2V0TGFiZWwiLCJldmVudFR5cGUiLCJzdGF0ZUtleSIsImV2Q29udGVudCIsIk9iamVjdCIsImFzc2lnbiIsImlucHV0cyIsImlzU3RhdGVFdmVudCIsIkJvb2xlYW4iLCJmb3JjZVN0YXRlRXZlbnQiLCJzZW5kIiwiY29udGVudCIsImNsaSIsImNvbnRleHQiLCJzZW5kU3RhdGVFdmVudCIsInJvb20iLCJyb29tSWQiLCJzZW5kRXZlbnQiLCJKU09OIiwicGFyc2UiLCJ0b1N0cmluZyIsInJlbmRlciIsInNob3dUZ2xGbGlwIiwiZm9yY2VHZW5lcmFsRXZlbnQiLCJmbG9hdCIsIlByb3BUeXBlcyIsImZ1bmMiLCJpc1JlcXVpcmVkIiwiaW5zdGFuY2VPZiIsIlJvb20iLCJib29sIiwib2JqZWN0IiwiTWF0cml4Q2xpZW50Q29udGV4dCIsIlNlbmRBY2NvdW50RGF0YSIsImlzUm9vbUFjY291bnREYXRhIiwic2V0Um9vbUFjY291bnREYXRhIiwic2V0QWNjb3VudERhdGEiLCJmb3JjZU1vZGUiLCJJTklUSUFMX0xPQURfVElMRVMiLCJMT0FEX1RJTEVTX1NURVBfU0laRSIsIkZpbHRlcmVkTGlzdCIsImZpbHRlckNoaWxkcmVuIiwiY2hpbGRyZW4iLCJxdWVyeSIsImxjUXVlcnkiLCJ0b0xvd2VyQ2FzZSIsImZpbHRlciIsImNoaWxkIiwia2V5IiwiaW5jbHVkZXMiLCJ0cnVuY2F0ZUF0Iiwib3ZlcmZsb3dDb3VudCIsInRvdGFsQ291bnQiLCJzaG93QWxsIiwiY291bnQiLCJldiIsIm9uQ2hhbmdlIiwic3RhcnQiLCJlbmQiLCJmaWx0ZXJlZENoaWxkcmVuIiwic2xpY2UiLCJsZW5ndGgiLCJVTlNBRkVfY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyIsIm5leHRQcm9wcyIsIlRydW5jYXRlZExpc3QiLCJzZGsiLCJnZXRDb21wb25lbnQiLCJvblF1ZXJ5IiwiZ2V0Q2hpbGRyZW4iLCJnZXRDaGlsZENvdW50IiwiY3JlYXRlT3ZlcmZsb3dFbGVtZW50IiwiYW55Iiwic3RyaW5nIiwiUm9vbVN0YXRlRXhwbG9yZXIiLCJyb29tU3RhdGVFdmVudHMiLCJjdXJyZW50U3RhdGUiLCJldmVudHMiLCJlZGl0RXYiLCJvblF1ZXJ5RXZlbnRUeXBlIiwib25RdWVyeVN0YXRlS2V5IiwiZXZlbnQiLCJlZGl0aW5nIiwicXVlcnlFdmVudFR5cGUiLCJxdWVyeVN0YXRlS2V5IiwiYnJvd3NlRXZlbnRUeXBlIiwib25WaWV3U291cmNlQ2xpY2siLCJmaWx0ZXJFdmVudFR5cGUiLCJmaWx0ZXJTdGF0ZUtleSIsImdldFR5cGUiLCJzdHJpbmdpZnkiLCJnZXRDb250ZW50IiwiZ2V0U3RhdGVLZXkiLCJsaXN0IiwiY2xhc3NlcyIsIkFycmF5IiwiZnJvbSIsImVudHJpZXMiLCJtYXAiLCJhbGxTdGF0ZUtleXMiLCJvbkNsaWNrRm4iLCJzaXplIiwiaGFzIiwiZ2V0Iiwic3RhdGVHcm91cCIsIkFjY291bnREYXRhRXhwbG9yZXIiLCJnZXREYXRhIiwiYWNjb3VudERhdGEiLCJzdG9yZSIsInJvd3MiLCJkYXRhIiwia2V5cyIsImZvckVhY2giLCJldlR5cGUiLCJwdXNoIiwiU2VydmVyc0luUm9vbUxpc3QiLCJzZXJ2ZXJzIiwiU2V0IiwiZ2V0U3RhdGVFdmVudHMiLCJhZGQiLCJnZXRTZW5kZXIiLCJzcGxpdCIsInMiLCJQSEFTRV9NQVAiLCJQSEFTRV9VTlNFTlQiLCJQSEFTRV9SRVFVRVNURUQiLCJQSEFTRV9SRUFEWSIsIlBIQVNFX0RPTkUiLCJQSEFTRV9TVEFSVEVEIiwiUEhBU0VfQ0FOQ0VMTEVEIiwiVmVyaWZpY2F0aW9uUmVxdWVzdCIsInR4bklkIiwicmVxdWVzdCIsInVwZGF0ZVN0YXRlIiwidGltZW91dCIsInNldFJlcXVlc3RUaW1lb3V0Iiwic2V0SW50ZXJ2YWwiLCJjbGVhckludGVydmFsIiwicGhhc2UiLCJNYXRoIiwiZmxvb3IiLCJtZXRob2RzIiwiam9pbiIsInJlcXVlc3RpbmdVc2VySWQiLCJvYnNlcnZlT25seSIsIlZlcmlmaWNhdGlvbkV4cGxvcmVyIiwiQ29tcG9uZW50IiwiZm9yY2VVcGRhdGUiLCJjb21wb25lbnREaWRNb3VudCIsIm9uIiwib25OZXdSZXF1ZXN0IiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJvZmYiLCJpblJvb21DaGFubmVsIiwiX2NyeXB0byIsIl9pblJvb21WZXJpZmljYXRpb25SZXF1ZXN0cyIsImluUm9vbVJlcXVlc3RzIiwiX3JlcXVlc3RzQnlSb29tSWQiLCJNYXAiLCJyZXZlcnNlIiwiV2lkZ2V0RXhwbG9yZXIiLCJ3aWRnZXQiLCJlZGl0V2lkZ2V0Iiwid2lkZ2V0cyIsIldpZGdldFN0b3JlIiwiaW5zdGFuY2UiLCJnZXRBcHBzIiwiVVBEQVRFX0VWRU5UIiwib25XaWRnZXRTdG9yZVVwZGF0ZSIsImFsbFN0YXRlIiwidmFsdWVzIiwicmVkdWNlIiwicCIsImMiLCJzdGF0ZUV2IiwiZmluZCIsImdldElkIiwiZXZlbnRJZCIsIm9uUXVlcnlDaGFuZ2UiLCJ3IiwidXJsIiwib25FZGl0V2lkZ2V0IiwiU2V0dGluZ3NFeHBsb3JlciIsImV4cGxpY2l0VmFsdWVz