diff --git a/src/script/Config.ts b/src/script/Config.ts index 98ae31239a4..37a5e6ef619 100644 --- a/src/script/Config.ts +++ b/src/script/Config.ts @@ -95,6 +95,9 @@ const Config = { getConfig: () => { return config; }, + _dangerouslySetConfigFeaturesForDebug: (newConfigFeatures: Configuration['FEATURE']) => { + (config.FEATURE as unknown) = newConfigFeatures; + }, getDesktopConfig: () => { if (!Runtime.isDesktopApp) { return undefined; diff --git a/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx b/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx new file mode 100644 index 00000000000..758bea4dade --- /dev/null +++ b/src/script/components/ConfigToolbar/ConfigToolbar.styles.tsx @@ -0,0 +1,33 @@ +/* + * Wire + * Copyright (C) 2022 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {CSSObject} from '@emotion/react'; + +export const wrapperStyles: CSSObject = { + position: 'fixed', + top: 0, + right: 0, + width: '400px', + height: '100vh', + backgroundColor: 'var(--sidebar-bg)', + padding: '20px', + overflow: 'auto', + zIndex: 100000000, + boxShadow: '3px 1px 6px 1px #000', +}; diff --git a/src/script/components/ConfigToolbar/ConfigToolbar.tsx b/src/script/components/ConfigToolbar/ConfigToolbar.tsx new file mode 100644 index 00000000000..ef02499814f --- /dev/null +++ b/src/script/components/ConfigToolbar/ConfigToolbar.tsx @@ -0,0 +1,128 @@ +/* + * Wire + * Copyright (C) 2024 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {useState, useEffect, useRef} from 'react'; + +import {Button, Input, Switch} from '@wireapp/react-ui-kit'; + +import {Config, Configuration} from 'src/script/Config'; +import {useClickOutside} from 'src/script/hooks/useClickOutside'; +import {isMetaKey} from 'Util/KeyboardUtil'; + +import {wrapperStyles} from './ConfigToolbar.styles'; + +export function ConfigToolbar() { + const [showConfig, setShowConfig] = useState(false); + const [configFeaturesState, setConfigFeaturesState] = useState(Config.getConfig().FEATURE); + const wrapperRef = useRef(null); + + // Toggle config tool on 'cmd/ctrl + shift + 2' + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (isMetaKey(event) && event.shiftKey && event.key === '2') { + setShowConfig(prev => !prev); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, []); + + // Update the config state when form input changes + const handleChange = (path: string, value: string | boolean | string[]) => { + const updateConfig = (obj: any, keys: string[]): void => { + if (keys.length === 1) { + obj[keys[0]] = value; + } else { + updateConfig(obj[keys[0]], keys.slice(1)); + } + }; + + const updatedConfig = {...configFeaturesState}; + updateConfig(updatedConfig, path.split('.')); + setConfigFeaturesState(updatedConfig); + Config._dangerouslySetConfigFeaturesForDebug(updatedConfig); + }; + + const renderInput = (value: string | boolean | string[] | number | object | null, path: string) => { + if (typeof value === 'boolean') { + return handleChange(path, isChecked)} />; + } + + if (Array.isArray(value)) { + return ( + + handleChange( + path, + event.currentTarget.value.split(',').map(value => value.trim()), + ) + } + /> + ); + } + + if (typeof value === 'object' && value !== null) { + return renderConfig(value, path); + } + + return ( + handleChange(path, event.currentTarget.value)} /> + ); + }; + + const renderConfig = (configObj: object, parentPath: string = '') => { + const entries = Object.entries(configObj); + + return entries.map(([key, value]) => { + const path = parentPath ? `${parentPath}.${key}` : key; + return ( +
+ + {renderInput(value, path)} +
+ ); + }); + }; + + useClickOutside(wrapperRef, () => setShowConfig(false)); + + if (!showConfig) { + return null; + } + + return ( +
+

Configuration Tool

+

+ Caution: Modifying these settings can affect the behavior of the application. Ensure you understand the + implications of each change before proceeding. Changes may cause unexpected behavior. +

+
{renderConfig(configFeaturesState)}
+

Debug Functions

+ + + +
+ ); +} diff --git a/src/script/page/AppMain.tsx b/src/script/page/AppMain.tsx index 0af1e84b2bd..53290ecb1f7 100644 --- a/src/script/page/AppMain.tsx +++ b/src/script/page/AppMain.tsx @@ -28,11 +28,13 @@ import {WebAppEvents} from '@wireapp/webapp-events'; import {CallingContainer} from 'Components/calling/CallingOverlayContainer'; import {ChooseScreen} from 'Components/calling/ChooseScreen'; +import {ConfigToolbar} from 'Components/ConfigToolbar/ConfigToolbar'; import {ErrorFallback} from 'Components/ErrorFallback'; import {GroupCreationModal} from 'Components/Modals/GroupCreation/GroupCreationModal'; import {LegalHoldModal} from 'Components/Modals/LegalHoldModal/LegalHoldModal'; import {PrimaryModal} from 'Components/Modals/PrimaryModal'; import {showUserModal, UserModal} from 'Components/Modals/UserModal'; +import {Config} from 'src/script/Config'; import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {AppLock} from './AppLock'; @@ -245,6 +247,7 @@ export const AppMain: FC = ({ {!locked && } + {Config.getConfig().FEATURE.ENABLE_DEBUG && } {!locked && (
{showLeftSidebar && (