feat(app): align default keybinds with Discord (#3501)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-15 23:04:13 +02:00
1 parent 9000a9f4c7
commit 41da6b5985
3 files changed
+41 -12

No files matched your search

@@ -114,12 +114,14 @@ import {
} from '@app/features/input/state/KeybindResolution'; } from '@app/features/input/state/KeybindResolution';
import { import {
DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO, DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO,
getKeyboardShortcutsOverlayComboForCurrentLayout, DEFAULT_START_DM_CALL_COMBO,
getKeyboardLayoutDefaultCombosForCurrentLayout,
keyCombosEqual, keyCombosEqual,
SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO, SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO,
} from '@app/features/input/utils/KeyboardShortcutLayoutUtils'; } from '@app/features/input/utils/KeyboardShortcutLayoutUtils';
import AppStorage from '@app/features/platform/state/PersistentStorage'; import AppStorage from '@app/features/platform/state/PersistentStorage';
import {awaitHydration, makePersistent} from '@app/features/platform/utils/MobXPersistence'; import {awaitHydration, makePersistent} from '@app/features/platform/utils/MobXPersistence';
import {isApplePlatform} from '@app/features/platform/utils/PlatformHelpers';
import {initializeStore} from '@app/features/platform/utils/StoreInitialization'; import {initializeStore} from '@app/features/platform/utils/StoreInitialization';
import {makeSyncedField} from '@app/features/user/state/SyncedField'; import {makeSyncedField} from '@app/features/user/state/SyncedField';
import UserSettings from '@app/features/user/state/UserSettings'; import UserSettings from '@app/features/user/state/UserSettings';
@@ -222,6 +224,7 @@ const createBuiltinDisableMarker = (
const getDefaultKeybinds = ( const getDefaultKeybinds = (
i18n: KeybindLabelI18n, i18n: KeybindLabelI18n,
keyboardShortcutsOverlayCombo: KeyCombo = DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO, keyboardShortcutsOverlayCombo: KeyCombo = DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO,
startDmCallCombo: KeyCombo = DEFAULT_START_DM_CALL_COMBO,
): ReadonlyArray<KeybindConfig> => ): ReadonlyArray<KeybindConfig> =>
[ [
{ {
@@ -360,7 +363,7 @@ const getDefaultKeybinds = (
{ {
action: 'nav_history_back', action: 'nav_history_back',
label: i18n._(MOVE_BACK_THROUGH_VIEWED_CHANNEL_HISTORY_DESCRIPTOR), label: i18n._(MOVE_BACK_THROUGH_VIEWED_CHANNEL_HISTORY_DESCRIPTOR),
combo: {key: '[', ctrlOrMeta: true}, combo: isApplePlatform() ? {key: '[', ctrlOrMeta: true} : {key: 'ArrowLeft', alt: true},
ignoreWhileTyping: true, ignoreWhileTyping: true,
editableFocusBehavior: 'allow', editableFocusBehavior: 'allow',
section: 'navigation', section: 'navigation',
@@ -368,7 +371,7 @@ const getDefaultKeybinds = (
{ {
action: 'nav_history_forward', action: 'nav_history_forward',
label: i18n._(MOVE_FORWARD_THROUGH_VIEWED_CHANNEL_HISTORY_DESCRIPTOR), label: i18n._(MOVE_FORWARD_THROUGH_VIEWED_CHANNEL_HISTORY_DESCRIPTOR),
combo: {key: ']', ctrlOrMeta: true}, combo: isApplePlatform() ? {key: ']', ctrlOrMeta: true} : {key: 'ArrowRight', alt: true},
ignoreWhileTyping: true, ignoreWhileTyping: true,
editableFocusBehavior: 'allow', editableFocusBehavior: 'allow',
section: 'navigation', section: 'navigation',
@@ -412,13 +415,13 @@ const getDefaultKeybinds = (
{ {
action: 'nav_guild_tab_prev', action: 'nav_guild_tab_prev',
label: i18n._(SWITCH_TO_PREVIOUS_COMMUNITY_OR_DMS_DESCRIPTOR), label: i18n._(SWITCH_TO_PREVIOUS_COMMUNITY_OR_DMS_DESCRIPTOR),
combo: {key: 'Tab', ctrlOrMeta: true, shift: true}, combo: {key: 'Tab', ctrl: true, shift: true},
section: 'navigation', section: 'navigation',
}, },
{ {
action: 'nav_guild_tab_next', action: 'nav_guild_tab_next',
label: i18n._(SWITCH_TO_NEXT_COMMUNITY_OR_DMS_DESCRIPTOR), label: i18n._(SWITCH_TO_NEXT_COMMUNITY_OR_DMS_DESCRIPTOR),
combo: {key: 'Tab', ctrlOrMeta: true}, combo: {key: 'Tab', ctrl: true},
section: 'navigation', section: 'navigation',
}, },
{ {
@@ -666,7 +669,7 @@ const getDefaultKeybinds = (
{ {
action: 'voice_start_dm_call', action: 'voice_start_dm_call',
label: i18n._(START_A_CALL_IN_A_DM_OR_GROUP_DESCRIPTOR), label: i18n._(START_A_CALL_IN_A_DM_OR_GROUP_DESCRIPTOR),
combo: {key: '`', code: 'Backquote', ctrl: true}, combo: {...startDmCallCombo},
section: 'voice_and_video', section: 'voice_and_video',
}, },
{ {
@@ -899,6 +902,7 @@ class Keybind {
private i18n: I18n | null = null; private i18n: I18n | null = null;
private initialized = false; private initialized = false;
private keyboardShortcutsOverlayCombo: KeyCombo = {...DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO}; private keyboardShortcutsOverlayCombo: KeyCombo = {...DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO};
private startDmCallCombo: KeyCombo = {...DEFAULT_START_DM_CALL_COMBO};
private holdSources = new HoldSources(() => this.pushToTalkReleaseDelay); private holdSources = new HoldSources(() => this.pushToTalkReleaseDelay);
constructor() { constructor() {
@@ -1011,9 +1015,10 @@ class Keybind {
async refreshKeyboardShortcutLayout(): Promise<void> { async refreshKeyboardShortcutLayout(): Promise<void> {
try { try {
const combo = await getKeyboardShortcutsOverlayComboForCurrentLayout(); const combos = await getKeyboardLayoutDefaultCombosForCurrentLayout();
if (!combo) return; if (!combos) return;
this.setKeyboardShortcutsOverlayCombo(combo); this.setKeyboardShortcutsOverlayCombo(combos.keyboardShortcutsOverlay);
this.setStartDmCallCombo(combos.startDmCall);
} catch {} } catch {}
} }
@@ -1028,8 +1033,15 @@ class Keybind {
}); });
} }
private setStartDmCallCombo(combo: KeyCombo): void {
if (keyCombosEqual(this.startDmCallCombo, combo)) return;
runInAction(() => {
this.startDmCallCombo = {...combo};
});
}
private get defaultKeybinds(): ReadonlyArray<KeybindConfig> { private get defaultKeybinds(): ReadonlyArray<KeybindConfig> {
return getDefaultKeybinds(this.getLabelI18n(), this.keyboardShortcutsOverlayCombo); return getDefaultKeybinds(this.getLabelI18n(), this.keyboardShortcutsOverlayCombo, this.startDmCallCombo);
} }
private get runtimeDispatchDefaults(): ReadonlyArray<KeybindConfig> { private get runtimeDispatchDefaults(): ReadonlyArray<KeybindConfig> {
@@ -211,6 +211,7 @@ export function formatPrimaryKeySymbol(i18n: I18n, rawKey: string): string {
if (rawKey === 'Enter' || rawKey === 'Return') return isMacPlatform() ? '⏎' : i18n._(ENTER_DESCRIPTOR); if (rawKey === 'Enter' || rawKey === 'Return') return isMacPlatform() ? '⏎' : i18n._(ENTER_DESCRIPTOR);
if (rawKey === 'Tab') return i18n._(TAB_DESCRIPTOR); if (rawKey === 'Tab') return i18n._(TAB_DESCRIPTOR);
if (rawKey === 'Backquote' || rawKey === '`') return '`'; if (rawKey === 'Backquote' || rawKey === '`') return '`';
if (rawKey === 'Quote') return "'";
if (rawKey === '[' || rawKey === ']') return rawKey; if (rawKey === '[' || rawKey === ']') return rawKey;
if (/^Key[A-Z]$/.test(rawKey)) return rawKey.slice(3); if (/^Key[A-Z]$/.test(rawKey)) return rawKey.slice(3);
if (/^Digit[0-9]$/.test(rawKey)) return rawKey.slice(5); if (/^Digit[0-9]$/.test(rawKey)) return rawKey.slice(5);
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
import type {KeyCombo} from '@app/features/input/state/InputKeybind'; import type {KeyCombo} from '@app/features/input/state/InputKeybind';
import {isPrintableShortcutKey} from '@fluxer/desktop_ipc/src/GlobalShortcutKeyNames';
interface KeyboardLayoutMapReader { interface KeyboardLayoutMapReader {
get(code: string): string | undefined; get(code: string): string | undefined;
@@ -18,6 +19,12 @@ export interface KeyboardShortcutsOverlayFallbackEvent {
export const DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO: KeyCombo = {key: '/', ctrlOrMeta: true}; export const DEFAULT_KEYBOARD_SHORTCUTS_OVERLAY_COMBO: KeyCombo = {key: '/', ctrlOrMeta: true};
export const SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO: KeyCombo = {key: '.', ctrlOrMeta: true}; export const SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO: KeyCombo = {key: '.', ctrlOrMeta: true};
export const DEFAULT_START_DM_CALL_COMBO: KeyCombo = {key: '', code: 'Quote', ctrl: true};
interface KeyboardLayoutDefaultCombos {
keyboardShortcutsOverlay: KeyCombo;
startDmCall: KeyCombo;
}
const NON_NUMPAD_PRINTABLE_LAYOUT_CODES = [ const NON_NUMPAD_PRINTABLE_LAYOUT_CODES = [
'Backquote', 'Backquote',
@@ -110,16 +117,25 @@ function getKeyboardShortcutsOverlayComboForLayoutMap(layoutMap: KeyboardLayoutM
return copyKeyCombo(SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO); return copyKeyCombo(SHIFTED_SLASH_FALLBACK_KEYBOARD_SHORTCUTS_OVERLAY_COMBO);
} }
function getStartDmCallComboForLayoutMap(layoutMap: KeyboardLayoutMapReader): KeyCombo {
const key = layoutMap.get('Quote');
if (!isPrintableShortcutKey(key)) return copyKeyCombo(DEFAULT_START_DM_CALL_COMBO);
return {...DEFAULT_START_DM_CALL_COMBO, key};
}
function getNavigatorKeyboardLayoutProvider(): KeyboardLayoutProvider | null { function getNavigatorKeyboardLayoutProvider(): KeyboardLayoutProvider | null {
if (typeof navigator === 'undefined') return null; if (typeof navigator === 'undefined') return null;
return (navigator as NavigatorWithKeyboardLayout).keyboard ?? null; return (navigator as NavigatorWithKeyboardLayout).keyboard ?? null;
} }
export async function getKeyboardShortcutsOverlayComboForCurrentLayout(): Promise<KeyCombo | null> { export async function getKeyboardLayoutDefaultCombosForCurrentLayout(): Promise<KeyboardLayoutDefaultCombos | null> {
const keyboard = getNavigatorKeyboardLayoutProvider(); const keyboard = getNavigatorKeyboardLayoutProvider();
if (typeof keyboard?.getLayoutMap !== 'function') return null; if (typeof keyboard?.getLayoutMap !== 'function') return null;
const layoutMap = await keyboard.getLayoutMap(); const layoutMap = await keyboard.getLayoutMap();
return getKeyboardShortcutsOverlayComboForLayoutMap(layoutMap); return {
keyboardShortcutsOverlay: getKeyboardShortcutsOverlayComboForLayoutMap(layoutMap),
startDmCall: getStartDmCallComboForLayoutMap(layoutMap),
};
} }
export function shouldUseKeyboardShortcutsOverlayFallbackFromEvent( export function shouldUseKeyboardShortcutsOverlayFallbackFromEvent(