Prevent accidental submits

This commit is contained in:
Half-Shot 2025-06-23 08:56:57 +01:00
parent 5982a4cfed
commit 452ff3b615
17 changed files with 150 additions and 128 deletions

View File

@ -98,7 +98,7 @@ const DevtoolsDialog: React.FC<IProps> = ({ roomId, threadRootId, onFinished })
})}
</div>
))}
<Form.Root>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<h3>{_t("common|options")}</h3>
<SettingsFlag name="developerMode" level={SettingLevel.ACCOUNT} />
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />

View File

@ -104,7 +104,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
let inviteToggle: JSX.Element | undefined;
if (this.isInviteOrKnockRoom) {
inviteToggle = (
<Form.Root>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsToggleInput
name="room-upgrade-warning"
checked={this.state.inviteUsersToNewRoom}

View File

@ -85,7 +85,7 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
onPrimaryButtonClick={this.onAllow}
onCancel={this.onDeny}
additive={
<Form.Root>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsToggleInput
name="remember-selection"
checked={this.state.rememberSelection}

View File

@ -191,7 +191,7 @@ const maximumVectorState = (
const NotificationActivitySettings = (): JSX.Element => {
return (
<Form.Root>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsFlag name="Notifications.showbold" level={SettingLevel.DEVICE} />
<SettingsFlag name="Notifications.tac_only_notifications" level={SettingLevel.DEVICE} />
</Form.Root>
@ -815,7 +815,7 @@ export default class Notifications extends React.PureComponent<EmptyObject, ISta
}
return (
<>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
{this.renderTopSection()}
{this.renderCategory(RuleClass.VectorGlobal)}
{this.renderCategory(RuleClass.VectorMentions)}
@ -823,7 +823,7 @@ export default class Notifications extends React.PureComponent<EmptyObject, ISta
{this.renderTargets()}
<NotificationActivitySettings />
{clearNotifsButton}
</>
</Form.Root>
);
}
}

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React from "react";
import { logger } from "matrix-js-sdk/src/logger";
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
import { Root, SettingsToggleInput } from "@vector-im/compound-web";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
@ -75,14 +75,12 @@ export default class SetIntegrationManager extends React.Component<EmptyObject,
</div>
<SettingsSubsectionText>{bodyText}</SettingsSubsectionText>
<SettingsSubsectionText>{_t("integration_manager|explainer")}</SettingsSubsectionText>
<Root>
<SettingsToggleInput
name="enable_im"
label={_t("integration_manager|toggle_label")}
checked={this.state.provisioningEnabled}
onChange={this.onProvisioningToggled}
/>
</Root>
<SettingsToggleInput
name="enable_im"
label={_t("integration_manager|toggle_label")}
checked={this.state.provisioningEnabled}
onChange={this.onProvisioningToggled}
/>
</div>
);
}

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type JSX, useState } from "react";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import NewAndImprovedIcon from "../../../../../res/img/element-icons/new-and-improved.svg";
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
@ -93,6 +93,7 @@ export default function NotificationSettings2(): JSX.Element {
return (
<div className="mx_NotificationSettings2">
{hasPendingChanges && model !== null && (
<SettingsBanner
icon={<img src={NewAndImprovedIcon} alt="" width={12} />}
@ -110,6 +111,7 @@ export default function NotificationSettings2(): JSX.Element {
</SettingsBanner>
)}
<SettingsSection>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<div className="mx_SettingsSubsection_content mx_NotificationSettings2_flags">
<SettingsToggleInput
name="enable_notifications_account"
@ -354,6 +356,7 @@ export default function NotificationSettings2(): JSX.Element {
{_t("settings|notifications|quick_actions_reset")}
</AccessibleButton>
</SettingsSubsection>
</Form.Root>
</SettingsSection>
</div>
);

View File

@ -37,9 +37,8 @@ export interface SettingsTabProps extends HTMLAttributes<HTMLDivElement> {
*/
const SettingsTab: React.FC<SettingsTabProps> = ({ children, className, ...rest }) => (
<div {...rest} className={classNames("mx_SettingsTab", className)}>
<Form.Root>
<div className="mx_SettingsTab_sections">{children}</div>
</Form.Root>
{/* Prevent settings from doing any accidental submits. */}
<div className="mx_SettingsTab_sections">{children}</div>
</div>
);

View File

@ -23,6 +23,7 @@ import SettingsTab from "../SettingsTab";
import { SettingsSection } from "../../shared/SettingsSection";
import { UrlPreviewSettings } from "../../../room_settings/UrlPreviewSettings";
import { MediaPreviewAccountSettings } from "../user/MediaPreviewAccountSettings";
import { Form } from "@vector-im/compound-web";
interface IProps {
room: Room;
@ -78,26 +79,28 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
return (
<SettingsTab data-testid="General">
<SettingsSection heading={_t("common|general")}>
<RoomProfileSettings roomId={room.roomId} />
</SettingsSection>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsSection heading={_t("common|general")}>
<RoomProfileSettings roomId={room.roomId} />
</SettingsSection>
<SettingsSection heading={_t("room_settings|general|aliases_section")}>
<AliasSettings
roomId={room.roomId}
canSetCanonicalAlias={canSetCanonical}
canSetAliases={canSetAliases}
canonicalAliasEvent={canonicalAliasEv}
/>
</SettingsSection>
<SettingsSection heading={_t("room_settings|general|aliases_section")}>
<AliasSettings
roomId={room.roomId}
canSetCanonicalAlias={canSetCanonical}
canSetAliases={canSetAliases}
canonicalAliasEvent={canonicalAliasEv}
/>
</SettingsSection>
<SettingsSection heading={_t("room_settings|general|other_section")}>
{urlPreviewSettings}
<SettingsSubsection heading={_t("common|moderation_and_safety")} legacy={false}>
<MediaPreviewAccountSettings roomId={room.roomId} />
</SettingsSubsection>
{leaveSection}
</SettingsSection>
<SettingsSection heading={_t("room_settings|general|other_section")}>
{urlPreviewSettings}
<SettingsSubsection heading={_t("common|moderation_and_safety")} legacy={false}>
<MediaPreviewAccountSettings roomId={room.roomId} />
</SettingsSubsection>
{leaveSection}
</SettingsSection>
</Form.Root>
</SettingsTab>
);
}

View File

@ -17,7 +17,7 @@ import {
EventType,
} from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { InlineSpinner, SettingsToggleInput } from "@vector-im/compound-web";
import { Form, InlineSpinner, SettingsToggleInput } from "@vector-im/compound-web";
import { Icon as WarningIcon } from "../../../../../../res/img/warning.svg";
import { _t } from "../../../../../languageHandler";
@ -446,6 +446,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
return (
<SettingsTab>
<SettingsSection heading={_t("room_settings|security|title")}>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsFieldset
legend={_t("settings|security|encryption_section")}
description={
@ -474,6 +475,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
</SettingsFieldset>
{this.renderJoinRule()}
{historySection}
</Form.Root>
</SettingsSection>
</SettingsTab>
);

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ChangeEventHandler, useCallback, useMemo, useState } from "react";
import { JoinRule, EventType, type RoomState, type Room } from "matrix-js-sdk/src/matrix";
import { type RoomPowerLevelsEventContent } from "matrix-js-sdk/src/types";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../../../languageHandler";
import { SettingsSubsection } from "../../shared/SettingsSubsection";
@ -96,9 +96,11 @@ export const VoipRoomSettingsTab: React.FC<Props> = ({ room }) => {
return (
<SettingsTab>
<SettingsSection heading={_t("settings|voip|title")}>
<SettingsSubsection heading={_t("room_settings|voip|call_type_section")}>
<ElementCallSwitch room={room} />
</SettingsSubsection>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsSubsection heading={_t("room_settings|voip|call_type_section")}>
<ElementCallSwitch room={room} />
</SettingsSubsection>
</Form.Root>
</SettingsSection>
</SettingsTab>
);

View File

@ -24,6 +24,7 @@ import ImageSizePanel from "../../ImageSizePanel";
import SettingsTab from "../SettingsTab";
import { SettingsSection } from "../../shared/SettingsSection";
import { SettingsSubsection } from "../../shared/SettingsSubsection";
import { Form } from "@vector-im/compound-web";
interface IState {
useBundledEmojiFont: boolean;
@ -102,11 +103,13 @@ export default class AppearanceUserSettingsTab extends React.Component<EmptyObje
return (
<SettingsTab data-testid="mx_AppearanceUserSettingsTab">
<SettingsSection>
<ThemeChoicePanel />
<LayoutSwitcher />
<FontScalingPanel />
{this.renderAdvancedSection()}
<ImageSizePanel />
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<ThemeChoicePanel />
<LayoutSwitcher />
<FontScalingPanel />
{this.renderAdvancedSection()}
<ImageSizePanel />
</Form.Root>
</SettingsSection>
</SettingsTab>
);

View File

@ -32,7 +32,7 @@ export const InviteRulesAccountSetting: FC = () => {
}
}, []);
return (
<Root className="mx_MediaPreviewAccountSetting_Form">
<Root className="mx_MediaPreviewAccountSetting_Form" onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsToggleInput
className="mx_MediaPreviewAccountSetting_ToggleSwitch"
name="invite_control_blocked"

View File

@ -20,6 +20,7 @@ import { EnhancedMap } from "../../../../../utils/maps";
import { SettingsSection } from "../../shared/SettingsSection";
import { SettingsSubsection, SettingsSubsectionText } from "../../shared/SettingsSubsection";
import SettingsTab from "../SettingsTab";
import { Form } from "@vector-im/compound-web";
export const showLabsFlags = (): boolean => {
return SdkConfig.get("show_labs_settings") || SettingsStore.getValue("developerMode");
@ -106,6 +107,7 @@ export default class LabsUserSettingsTab extends React.Component<EmptyObject> {
return (
<SettingsTab>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsSection heading={_t("labs|beta_section")}>
<SettingsSubsectionText>
{_t("labs|beta_description", { brand: SdkConfig.get("brand") })}
@ -137,6 +139,7 @@ export default class LabsUserSettingsTab extends React.Component<EmptyObject> {
{labsSections}
</SettingsSection>
)}
</Form.Root>
</SettingsTab>
);
}

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
*/
import React, { type ChangeEventHandler, type JSX, type ReactElement, useCallback, useEffect, useState } from "react";
import { SettingsToggleControl } from "@vector-im/compound-web";
import { Form, SettingsToggleControl } from "@vector-im/compound-web";
import { type NonEmptyArray } from "../../../../../@types/common";
import { _t, getCurrentLanguage } from "../../../../../languageHandler";
@ -258,6 +258,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
return (
<SettingsTab data-testid="mx_PreferencesUserSettingsTab">
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsSection>
{/* The heading string is still 'general' from where it was moved, but this section should become 'general' */}
<SettingsSubsection heading={_t("settings|general|language_section")}>
@ -390,6 +391,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
/>
</SettingsSubsection>
</SettingsSection>
</Form.Root>
</SettingsTab>
);
}

View File

@ -32,6 +32,7 @@ import { SettingsSubsection, SettingsSubsectionText } from "../../shared/Setting
import { useOwnDevices } from "../../devices/useOwnDevices";
import { DiscoverySettings } from "../../discovery/DiscoverySettings";
import SetIntegrationManager from "../../SetIntegrationManager";
import { Form } from "@vector-im/compound-web";
interface IIgnoredUserProps {
userId: string;
@ -355,17 +356,19 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
return (
<SettingsTab>
{warning}
<SetIntegrationManager />
<SettingsSection heading={_t("settings|security|encryption_section")}>
{secureBackup}
{eventIndex}
</SettingsSection>
<SettingsSection heading={_t("common|privacy")}>
<DiscoverySettings />
{posthogSection}
</SettingsSection>
{advancedSection}
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
{warning}
<SetIntegrationManager />
<SettingsSection heading={_t("settings|security|encryption_section")}>
{secureBackup}
{eventIndex}
</SettingsSection>
<SettingsSection heading={_t("common|privacy")}>
<DiscoverySettings />
{posthogSection}
</SettingsSection>
{advancedSection}
</Form.Root>
</SettingsTab>
);
}

View File

@ -11,7 +11,7 @@ import React, { type ChangeEventHandler, type JSX, type ReactNode } from "react"
import { logger } from "matrix-js-sdk/src/logger";
import { FALLBACK_ICE_SERVER } from "matrix-js-sdk/src/webrtc/call";
import { type EmptyObject } from "matrix-js-sdk/src/matrix";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../../../languageHandler";
import MediaDeviceHandler, { type IMediaDevices, MediaDeviceKindEnum } from "../../../../../MediaDeviceHandler";
@ -187,55 +187,57 @@ export default class VoiceUserSettingsTab extends React.Component<EmptyObject, I
return (
<SettingsTab>
<SettingsSection>
{requestButton}
<SettingsSubsection heading={_t("settings|voip|voice_section")} stretchContent>
{speakerDropdown}
{microphoneDropdown}
<SettingsToggleInput
name="voice-auto-gain"
label={_t("settings|voip|voice_agc")}
checked={this.state.audioAutoGainControl}
onChange={this.onAutoGainChanged}
/>
</SettingsSubsection>
<SettingsSubsection heading={_t("settings|voip|video_section")} stretchContent>
{webcamDropdown}
<SettingsFlag name="VideoView.flipVideoHorizontally" level={SettingLevel.ACCOUNT} />
</SettingsSubsection>
</SettingsSection>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsSection>
{requestButton}
<SettingsSubsection heading={_t("settings|voip|voice_section")} stretchContent>
{speakerDropdown}
{microphoneDropdown}
<SettingsToggleInput
name="voice-auto-gain"
label={_t("settings|voip|voice_agc")}
checked={this.state.audioAutoGainControl}
onChange={this.onAutoGainChanged}
/>
</SettingsSubsection>
<SettingsSubsection heading={_t("settings|voip|video_section")} stretchContent>
{webcamDropdown}
<SettingsFlag name="VideoView.flipVideoHorizontally" level={SettingLevel.ACCOUNT} />
</SettingsSubsection>
</SettingsSection>
<SettingsSection heading={_t("common|advanced")}>
<SettingsSubsection heading={_t("settings|voip|voice_processing")}>
<SettingsToggleInput
name="voice-noise-suppression"
label={_t("settings|voip|noise_suppression")}
checked={this.state.audioNoiseSuppression}
onChange={this.onNoiseSuppressionChanged}
/>
<SettingsToggleInput
name="voice-echo-cancellation"
label={_t("settings|voip|echo_cancellation")}
checked={this.state.audioEchoCancellation}
onChange={this.onEchoCancellationChanged}
/>
</SettingsSubsection>
<SettingsSubsection heading={_t("settings|voip|connection_section")}>
<SettingsFlag
name="webRtcAllowPeerToPeer"
level={SettingLevel.DEVICE}
onChange={this.changeWebRtcMethod}
/>
<SettingsFlag
name="fallbackICEServerAllowed"
label={_t("settings|voip|enable_fallback_ice_server", {
server: new URL(FALLBACK_ICE_SERVER).pathname,
})}
level={SettingLevel.DEVICE}
hideIfCannotSet
/>
</SettingsSubsection>
</SettingsSection>
<SettingsSection heading={_t("common|advanced")}>
<SettingsSubsection heading={_t("settings|voip|voice_processing")}>
<SettingsToggleInput
name="voice-noise-suppression"
label={_t("settings|voip|noise_suppression")}
checked={this.state.audioNoiseSuppression}
onChange={this.onNoiseSuppressionChanged}
/>
<SettingsToggleInput
name="voice-echo-cancellation"
label={_t("settings|voip|echo_cancellation")}
checked={this.state.audioEchoCancellation}
onChange={this.onEchoCancellationChanged}
/>
</SettingsSubsection>
<SettingsSubsection heading={_t("settings|voip|connection_section")}>
<SettingsFlag
name="webRtcAllowPeerToPeer"
level={SettingLevel.DEVICE}
onChange={this.changeWebRtcMethod}
/>
<SettingsFlag
name="fallbackICEServerAllowed"
label={_t("settings|voip|enable_fallback_ice_server", {
server: new URL(FALLBACK_ICE_SERVER).pathname,
})}
level={SettingLevel.DEVICE}
hideIfCannotSet
/>
</SettingsSubsection>
</SettingsSection>
</Form.Root>
</SettingsTab>
);
}

View File

@ -15,7 +15,7 @@ import {
JoinRule,
type MatrixClient,
} from "matrix-js-sdk/src/matrix";
import { SettingsToggleInput } from "@vector-im/compound-web";
import { Form, SettingsToggleInput } from "@vector-im/compound-web";
import { _t } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton";
@ -160,24 +160,26 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
/>
{advancedSection}
<div className="mx_SettingsTab_toggleWithDescription">
<SettingsToggleInput
name="space-history-visibility"
checked={historyVisibility === HistoryVisibility.WorldReadable}
onChange={(evt): void => {
setHistoryVisibility(
evt.target.checked ? HistoryVisibility.WorldReadable : HistoryVisibility.Shared,
);
}}
helpMessage={_t("room_settings|visibility|history_visibility_anyone_space_description")}
disabled={!canSetHistoryVisibility}
disabledMessage={_t("room_settings|visibility|history_visibility_anyone_space_disabled")}
label={_t("room_settings|visibility|history_visibility_anyone_space")}
/>
<p>
<strong>
{_t("room_settings|visibility|history_visibility_anyone_space_recommendation")}
</strong>
</p>
<Form.Root onSubmit={(evt) => {evt.preventDefault(); evt.stopPropagation();}}>
<SettingsToggleInput
name="space-history-visibility"
checked={historyVisibility === HistoryVisibility.WorldReadable}
onChange={(evt): void => {
setHistoryVisibility(
evt.target.checked ? HistoryVisibility.WorldReadable : HistoryVisibility.Shared,
);
}}
helpMessage={_t("room_settings|visibility|history_visibility_anyone_space_description")}
disabled={!canSetHistoryVisibility}
disabledMessage={_t("room_settings|visibility|history_visibility_anyone_space_disabled")}
label={_t("room_settings|visibility|history_visibility_anyone_space")}
/>
<p>
<strong>
{_t("room_settings|visibility|history_visibility_anyone_space_recommendation")}
</strong>
</p>
</Form.Root>
</div>
</SettingsFieldset>