/* Copyright 2024 New Vector Ltd. Copyright 2018-2023 The Matrix.org Foundation C.I.C. Copyright 2022 Michael Telatynski <7t3chguy@gmail.com> SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React, { ChangeEvent, useContext, useMemo, useState } from "react"; import { logger } from "matrix-js-sdk/src/logger"; import { _t, _td } from "../../../../languageHandler"; import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import AccessibleButton from "../../elements/AccessibleButton"; import SettingsStore, { LEVEL_ORDER } from "../../../../settings/SettingsStore"; import { SettingLevel } from "../../../../settings/SettingLevel"; import { SETTINGS } from "../../../../settings/Settings"; import Field from "../../elements/Field"; const SettingExplorer: React.FC = ({ onBack }) => { const [setting, setSetting] = useState(null); const [editing, setEditing] = useState(false); if (setting && editing) { const onBack = (): void => { setEditing(false); }; return ; } else if (setting) { const onBack = (): void => { setSetting(null); }; const onEdit = async (): Promise => { setEditing(true); }; return ; } else { const onView = (setting: string): void => { setSetting(setting); }; const onEdit = (setting: string): void => { setSetting(setting); setEditing(true); }; return ; } }; export default SettingExplorer; interface ICanEditLevelFieldProps { setting: string; level: SettingLevel; roomId?: string; } const CanEditLevelField: React.FC = ({ setting, roomId, level }) => { const canEdit = SettingsStore.canSetValue(setting, roomId ?? null, level); const className = canEdit ? "mx_DevTools_SettingsExplorer_mutable" : "mx_DevTools_SettingsExplorer_immutable"; return ( {canEdit.toString()} ); }; function renderExplicitSettingValues(setting: string, roomId?: string): string { const vals: Record = {}; for (const level of LEVEL_ORDER) { try { vals[level] = SettingsStore.getValueAt(level, setting, roomId, true, true); if (vals[level] === undefined) { vals[level] = null; } } catch (e) { logger.warn(e); } } return JSON.stringify(vals, null, 4); } interface IEditSettingProps extends Pick { setting: string; } const EditSetting: React.FC = ({ setting, onBack }) => { const context = useContext(DevtoolsContext); const [explicitValue, setExplicitValue] = useState(renderExplicitSettingValues(setting)); const [explicitRoomValue, setExplicitRoomValue] = useState( renderExplicitSettingValues(setting, context.room.roomId), ); const onSave = async (): Promise => { try { const parsedExplicit = JSON.parse(explicitValue); const parsedExplicitRoom = JSON.parse(explicitRoomValue); for (const level of Object.keys(parsedExplicit)) { logger.log(`[Devtools] Setting value of ${setting} at ${level} from user input`); try { const val = parsedExplicit[level]; await SettingsStore.setValue(setting, null, level as SettingLevel, val); } catch (e) { logger.warn(e); } } const roomId = context.room.roomId; for (const level of Object.keys(parsedExplicit)) { logger.log(`[Devtools] Setting value of ${setting} at ${level} in ${roomId} from user input`); try { const val = parsedExplicitRoom[level]; await SettingsStore.setValue(setting, roomId, level as SettingLevel, val); } catch (e) { logger.warn(e); } } onBack(); } catch (e) { return _t("devtools|failed_to_save") + (e instanceof Error ? ` (${e.message})` : ""); } }; return (

{_t("devtools|setting_colon")} {setting}

{_t("devtools|caution_colon")} {_t("devtools|use_at_own_risk")}
{_t("devtools|setting_definition")}
                    {JSON.stringify(SETTINGS[setting], null, 4)}
                
{LEVEL_ORDER.map((lvl) => ( ))}
{_t("devtools|level")} {_t("devtools|settable_global")} {_t("devtools|settable_room")}
{lvl}
setExplicitValue(e.target.value)} />
setExplicitRoomValue(e.target.value)} />
); }; interface IViewSettingProps extends Pick { setting: string; onEdit(): Promise; } const ViewSetting: React.FC = ({ setting, onEdit, onBack }) => { const context = useContext(DevtoolsContext); return (

{_t("devtools|setting_colon")} {setting}

{_t("devtools|setting_definition")}
                    {JSON.stringify(SETTINGS[setting], null, 4)}
                
{_t("devtools|value_colon")}  {renderSettingValue(SettingsStore.getValue(setting))}
{_t("devtools|value_this_room_colon")}  {renderSettingValue(SettingsStore.getValue(setting, context.room.roomId))}
{_t("devtools|values_explicit_colon")}
                    {renderExplicitSettingValues(setting)}
                
{_t("devtools|values_explicit_this_room_colon")}
                    {renderExplicitSettingValues(setting, context.room.roomId)}
                
); }; function renderSettingValue(val: any): string { // 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); } } interface ISettingsListProps extends Pick { onView(setting: string): void; onEdit(setting: string): void; } const SettingsList: React.FC = ({ onBack, onView, onEdit }) => { const context = useContext(DevtoolsContext); const [query, setQuery] = useState(""); const allSettings = useMemo(() => { let allSettings = Object.keys(SETTINGS); if (query) { const lcQuery = query.toLowerCase(); allSettings = allSettings.filter((setting) => setting.toLowerCase().includes(lcQuery)); } return allSettings; }, [query]); return ( ) => setQuery(ev.target.value)} className="mx_TextInputDialog_input mx_DevTools_RoomStateExplorer_query" /> {allSettings.map((i) => ( ))}
{_t("devtools|setting_id")} {_t("devtools|value")} {_t("devtools|value_in_this_room")}
onView(i)} > {i} onEdit(i)} className="mx_DevTools_SettingsExplorer_edit" > ✏ {renderSettingValue(SettingsStore.getValue(i))} {renderSettingValue(SettingsStore.getValue(i, context.room.roomId))}
); };