Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@
"preferences_tab": {
"developer_mode_label": "Developer mode",
"developer_mode_label_description": "Enable developer mode and show developer settings tab.",
"enable_keyboard_shortcuts_description": "Use keyboard shortcuts to control Element Call.",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The product name is configurable so it'll need to be templated in

"enable_keyboard_shortcuts_label": "Enable keyboard shortcuts",
"introduction": "Here you can configure extra options for an improved experience.",
"reactions_play_sound_description": "Play a sound effect when anyone sends a reaction into a call.",
"reactions_play_sound_label": "Play reaction sounds",
Expand Down
18 changes: 18 additions & 0 deletions src/settings/PreferencesSettingsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
Copyright (C) 2025 Element Creations Ltd
Copyright 2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Expand All @@ -15,6 +16,7 @@ import {
showReactions as showReactionsSetting,
playReactionsSound as playReactionsSoundSetting,
developerMode as developerModeSetting,
enableKeyboardShortcuts as enableKeyboardShortcutsSetting,
useSetting,
} from "./settings";

Expand All @@ -39,6 +41,10 @@ export const PreferencesSettingsTab: FC = () => {

const [developerMode, setDeveloperMode] = useSetting(developerModeSetting);

const [enableKeyboardShortcuts, setEnableKeyboardShortcuts] = useSetting(
enableKeyboardShortcutsSetting,
);

return (
<div>
<Text>{t("settings.preferences_tab.introduction")}</Text>
Expand All @@ -64,6 +70,18 @@ export const PreferencesSettingsTab: FC = () => {
onChange={(e) => onChangeSetting(e, setShowReactions)}
/>
</FieldRow>
<FieldRow>
<InputField
id="enableKeyboardShortcuts"
label={t("settings.preferences_tab.enable_keyboard_shortcuts_label")}
description={t(
"settings.preferences_tab.enable_keyboard_shortcuts_description",
)}
type="checkbox"
checked={enableKeyboardShortcuts}
onChange={(e) => onChangeSetting(e, setEnableKeyboardShortcuts)}
/>
</FieldRow>
<FieldRow>
<InputField
id="playReactionSound"
Expand Down
6 changes: 6 additions & 0 deletions src/settings/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
Copyright (C) 2025 Element Creations Ltd
Copyright 2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Expand Down Expand Up @@ -105,6 +106,11 @@ export const showHandRaisedTimer = new Setting<boolean>(

export const showReactions = new Setting<boolean>("reactions-show", true);

export const enableKeyboardShortcuts = new Setting<boolean>(
"keyboard-shortcuts",
true,
);

export const playReactionsSound = new Setting<boolean>(
"reactions-play-sound",
true,
Expand Down
11 changes: 9 additions & 2 deletions src/useCallViewKeyboardShortcuts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
Copyright (C) 2025 Element Creations Ltd
Copyright 2022-2024 New Vector Ltd.

SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Expand All @@ -13,6 +14,7 @@ import {
ReactionSet,
ReactionsRowSize,
} from "./reactions";
import { enableKeyboardShortcuts, useSetting } from "./settings/settings";

/**
* Determines whether focus is in the same part of the tree as the given
Expand All @@ -35,6 +37,7 @@ export function useCallViewKeyboardShortcuts(
sendReaction: (reaction: ReactionOption) => void,
toggleHandRaised: () => void,
): void {
const [shortcutsEnabled] = useSetting(enableKeyboardShortcuts);
const spacebarHeld = useRef(false);

// These event handlers are set on the window because we want users to be able
Expand All @@ -45,6 +48,7 @@ export function useCallViewKeyboardShortcuts(
"keydown",
useCallback(
(event: KeyboardEvent) => {
if (!shortcutsEnabled) return;
if (focusElement.current === null) return;
if (!mayReceiveKeyEvents(focusElement.current)) return;
if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
Expand Down Expand Up @@ -77,6 +81,7 @@ export function useCallViewKeyboardShortcuts(
setAudioEnabled,
sendReaction,
toggleHandRaised,
shortcutsEnabled,
],
),
// Because this is set on the window, to prevent shortcuts from activating
Expand All @@ -90,6 +95,7 @@ export function useCallViewKeyboardShortcuts(
"keyup",
useCallback(
(event: KeyboardEvent) => {
if (!shortcutsEnabled) return;
if (focusElement.current === null) return;
if (!mayReceiveKeyEvents(focusElement.current)) return;

Expand All @@ -98,18 +104,19 @@ export function useCallViewKeyboardShortcuts(
setAudioEnabled?.(false);
}
},
[focusElement, setAudioEnabled],
[shortcutsEnabled, focusElement, setAudioEnabled],
),
);

useEventTarget(
window,
"blur",
useCallback(() => {
if (!shortcutsEnabled) return;
if (spacebarHeld.current) {
spacebarHeld.current = false;
setAudioEnabled?.(true);
}
}, [setAudioEnabled, spacebarHeld]),
}, [setAudioEnabled, spacebarHeld, shortcutsEnabled]),
);
}
Loading