mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 13:05:32 +09:00
fix(app): make disabling built-in shortcuts take effect live (#3096)
This commit is contained in:
1 parent
2df82b2b5e
commit
9a074adb11
45 files changed
+2476
-1424
No files matched your search
+16
@@ -63,6 +63,22 @@ const CUSTOM_KEYBIND_ACTION_LABEL_DESCRIPTORS: Partial<Record<KeybindCommand, Me
|
||||
message: 'Move item down',
|
||||
comment: 'Label in the keybinds tab action dropdown. Distinguishes up from down.',
|
||||
}),
|
||||
message_reply_prev: msg({
|
||||
message: 'Reply to message above',
|
||||
comment: 'Label in the keybinds tab action dropdown. Distinguishes above from below.',
|
||||
}),
|
||||
message_reply_next: msg({
|
||||
message: 'Reply to message below',
|
||||
comment: 'Label in the keybinds tab action dropdown. Distinguishes above from below.',
|
||||
}),
|
||||
message_edit_prev: msg({
|
||||
message: 'Edit your message above',
|
||||
comment: 'Label in the keybinds tab action dropdown. Distinguishes above from below.',
|
||||
}),
|
||||
message_edit_next: msg({
|
||||
message: 'Edit your message below',
|
||||
comment: 'Label in the keybinds tab action dropdown. Distinguishes above from below.',
|
||||
}),
|
||||
};
|
||||
|
||||
export function getCustomKeybindActionLabel(i18n: I18n, action: KeybindCommand, fallbackLabel?: string): string {
|
||||
|
||||
+7
-2
@@ -2,6 +2,7 @@
|
||||
|
||||
import {ConfirmModal} from '@app/features/app/components/dialogs/ConfirmModal';
|
||||
import Keybind from '@app/features/input/state/InputKeybind';
|
||||
import {isBuiltinDisableMarker} from '@app/features/input/state/KeybindResolution';
|
||||
import {remFromPx} from '@app/features/theme/layout/RemFromPx';
|
||||
import {RetryIcon} from '@app/features/ui/action_menu/ContextMenuIcons';
|
||||
import {Button} from '@app/features/ui/button/Button';
|
||||
@@ -57,7 +58,11 @@ const SHORTCUTS_ARE_PAUSED_WHILE_RECORDING_DESCRIPTOR = msg({
|
||||
});
|
||||
export const CustomKeybindsList: React.FC<{searchQuery: string}> = observer(({searchQuery}) => {
|
||||
const {i18n} = useLingui();
|
||||
const customKeybinds = Keybind.getCustomKeybinds();
|
||||
const allCustomKeybinds = Keybind.getCustomKeybinds();
|
||||
const customKeybinds = useMemo(
|
||||
() => allCustomKeybinds.filter((entry) => !isBuiltinDisableMarker(entry)),
|
||||
[allCustomKeybinds],
|
||||
);
|
||||
const syncAcrossDevices = Keybind.getSyncAcrossDevices();
|
||||
const disableBuiltinKeybinds = Keybind.getDisableBuiltinKeybinds();
|
||||
const labelByAction = getCustomKeybindActionLabelMap(i18n, Keybind.getDefaults());
|
||||
@@ -110,7 +115,7 @@ export const CustomKeybindsList: React.FC<{searchQuery: string}> = observer(({se
|
||||
)),
|
||||
);
|
||||
};
|
||||
const hasAnyOverrides = customKeybinds.length > 0 || disableBuiltinKeybinds;
|
||||
const hasAnyOverrides = allCustomKeybinds.length > 0 || disableBuiltinKeybinds;
|
||||
return (
|
||||
<div className={styles.customSection} data-flx="user.keybinds-tab.custom-keybinds-list.custom-section">
|
||||
<div className={styles.customHeader} data-flx="user.keybinds-tab.custom-keybinds-list.custom-header">
|
||||
|
||||
+9
-7
@@ -5,6 +5,7 @@ import Keybind, {
|
||||
type KeybindConfig,
|
||||
type KeybindSection,
|
||||
} from '@app/features/input/state/InputKeybind';
|
||||
import {isActiveCustomKeybind, isBuiltinDisableMarker} from '@app/features/input/state/KeybindResolution';
|
||||
import styles from '@app/features/user/components/modals/tabs/KeybindsTab.module.css';
|
||||
import {DefaultShortcutRow} from '@app/features/user/components/modals/tabs/keybinds_tab/DefaultShortcutRow';
|
||||
import {
|
||||
@@ -52,13 +53,13 @@ export const DefaultKeybindsList: React.FC<{searchQuery: string}> = observer(({s
|
||||
const {i18n} = useLingui();
|
||||
const defaults = Keybind.getDefaults();
|
||||
const customKeybinds = Keybind.getCustomKeybinds();
|
||||
const overriddenActions = useMemo(() => {
|
||||
const set = new Set<KeybindCommand>();
|
||||
for (const entry of customKeybinds) {
|
||||
if (entry.action) set.add(entry.action);
|
||||
}
|
||||
return set;
|
||||
}, [customKeybinds]);
|
||||
const overriddenActions = new Set<KeybindCommand>();
|
||||
const disabledActions = new Set<KeybindCommand>();
|
||||
for (const entry of customKeybinds) {
|
||||
if (!entry.action) continue;
|
||||
if (isActiveCustomKeybind(entry)) overriddenActions.add(entry.action);
|
||||
else if (isBuiltinDisableMarker(entry)) disabledActions.add(entry.action);
|
||||
}
|
||||
const normalized = normalizeQuery(searchQuery);
|
||||
const sectionLabels: Record<KeybindSection, string> = {
|
||||
defaults: i18n._(DEFAULT_SHORTCUTS_DESCRIPTOR),
|
||||
@@ -124,6 +125,7 @@ export const DefaultKeybindsList: React.FC<{searchQuery: string}> = observer(({s
|
||||
key={Array.isArray(row) ? `${row[0].action}-${row[1].action}-${idx}` : `${row.action}-${idx}`}
|
||||
row={row}
|
||||
overriddenActions={overriddenActions}
|
||||
disabledActions={disabledActions}
|
||||
data-flx="user.keybinds-tab.default-keybinds-list.default-shortcut-row"
|
||||
/>
|
||||
))}
|
||||
|
||||
+10
-2
@@ -5,9 +5,10 @@ import {OVERRIDDEN_CHIP_STYLE} from '@app/features/user/components/modals/tabs/k
|
||||
import {Trans} from '@lingui/react/macro';
|
||||
import type React from 'react';
|
||||
|
||||
export const DefaultShortcutChipList: React.FC<{chips: Array<string>; overridden?: boolean}> = ({
|
||||
export const DefaultShortcutChipList: React.FC<{chips: Array<string>; overridden?: boolean; disabled?: boolean}> = ({
|
||||
chips,
|
||||
overridden,
|
||||
disabled,
|
||||
}) => (
|
||||
<div className={styles.defaultChips} data-flx="user.keybinds-tab.default-shortcut-chip-list.default-chips">
|
||||
{chips.length === 0 ? (
|
||||
@@ -22,7 +23,7 @@ export const DefaultShortcutChipList: React.FC<{chips: Array<string>; overridden
|
||||
<span
|
||||
key={`${idx}-${chip}`}
|
||||
className={styles.defaultChip}
|
||||
style={overridden ? OVERRIDDEN_CHIP_STYLE : undefined}
|
||||
style={overridden || disabled ? OVERRIDDEN_CHIP_STYLE : undefined}
|
||||
data-flx="user.keybinds-tab.default-shortcut-chip-list.default-chip"
|
||||
>
|
||||
{chip}
|
||||
@@ -36,6 +37,13 @@ export const DefaultShortcutChipList: React.FC<{chips: Array<string>; overridden
|
||||
>
|
||||
<Trans>Overridden</Trans>
|
||||
</span>
|
||||
) : disabled ? (
|
||||
<span
|
||||
className={styles.defaultChipsEmpty}
|
||||
data-flx="user.keybinds-tab.default-shortcut-chip-list.default-chips-empty--3"
|
||||
>
|
||||
<Trans comment="Status next to a built-in shortcut the user turned off in the keybinds tab.">Disabled</Trans>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
+82
-55
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import Keybind, {type KeybindCommand} from '@app/features/input/state/InputKeybind';
|
||||
import {isBuiltinDisableMarker} from '@app/features/input/state/KeybindResolution';
|
||||
import {CheckboxItem} from '@app/features/ui/action_menu/ContextMenu';
|
||||
import {
|
||||
EditSimpleIcon,
|
||||
@@ -46,6 +47,11 @@ const RESET_TO_BUILT_IN_2_DESCRIPTOR = msg({
|
||||
comment:
|
||||
'Button or menu action label in the keybinds tab. Keep it concise. Preserve {label}; it is inserted by code.',
|
||||
});
|
||||
const RE_ENABLE_BUILT_IN_SHORTCUT_DESCRIPTOR = msg({
|
||||
message: 'Re-enable built-in shortcut',
|
||||
comment:
|
||||
'Button or menu action label in the keybinds tab. Turns a disabled built-in shortcut back on. Keep it concise.',
|
||||
});
|
||||
const SET_CUSTOM_SHORTCUT_FOR_DESCRIPTOR = msg({
|
||||
message: 'Set custom shortcut for "{label}"',
|
||||
comment: 'Label in the keybinds tab. Preserve {label}; it is inserted by code.',
|
||||
@@ -58,29 +64,47 @@ const GLOBAL_SHORTCUT_DESCRIPTOR = msg({
|
||||
message: 'Global shortcut',
|
||||
comment: 'Toggle in the keybinds tab. When on, the shortcut works even when Fluxer is not focused. Keep it concise.',
|
||||
});
|
||||
export const DefaultShortcutRow: React.FC<{row: ShortcutRowModel; overriddenActions: ReadonlySet<KeybindCommand>}> = ({
|
||||
row,
|
||||
overriddenActions,
|
||||
}) => {
|
||||
export const DefaultShortcutRow: React.FC<{
|
||||
row: ShortcutRowModel;
|
||||
overriddenActions: ReadonlySet<KeybindCommand>;
|
||||
disabledActions: ReadonlySet<KeybindCommand>;
|
||||
}> = ({row, overriddenActions, disabledActions}) => {
|
||||
const {i18n} = useLingui();
|
||||
const actions = getRowActions(row);
|
||||
const openRowMenu = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
ContextMenuCommands.openFromEvent(event, ({onClose}) => {
|
||||
const perAction = actions.map((action) => {
|
||||
const customBindings = Keybind.getCustomKeybinds().filter((entry) => entry.action === action.action);
|
||||
const hasEnabledCustomBinding = customBindings.some((entry) => entry.enabled);
|
||||
return {
|
||||
action,
|
||||
label: getCustomKeybindActionLabel(i18n, action.action, action.label),
|
||||
customBindings,
|
||||
hasEnabledCustomBinding,
|
||||
};
|
||||
});
|
||||
const allHaveCustom = perAction.every((p) => p.customBindings.length > 0);
|
||||
const noneHaveCustom = perAction.every((p) => p.customBindings.length === 0);
|
||||
const perAction = actions.map((action) => ({
|
||||
action,
|
||||
label: getCustomKeybindActionLabel(i18n, action.action, action.label),
|
||||
hasCustomBinding: Keybind.getCustomKeybinds().some(
|
||||
(entry) => entry.action === action.action && !isBuiltinDisableMarker(entry),
|
||||
),
|
||||
builtinDisabled: Keybind.isBuiltinDisabled(action.action),
|
||||
}));
|
||||
const noneHaveCustom = perAction.every((p) => !p.hasCustomBinding);
|
||||
const anyBuiltinDisabled = perAction.some((p) => p.builtinDisabled);
|
||||
const allBuiltinDisabled = perAction.every((p) => p.builtinDisabled);
|
||||
const globalAction =
|
||||
actions.length === 1 && Keybind.isActionGlobalCapable(actions[0].action) ? actions[0].action : null;
|
||||
actions.length === 1 && !anyBuiltinDisabled && Keybind.isActionGlobalCapable(actions[0].action)
|
||||
? actions[0].action
|
||||
: null;
|
||||
const reEnableGroup = anyBuiltinDisabled ? (
|
||||
<MenuGroup data-flx="user.keybinds-tab.open-row-menu.re-enable-menu-group">
|
||||
<MenuItem
|
||||
icon={<RetryIcon size={16} data-flx="user.keybinds-tab.open-row-menu.retry-icon--3" />}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
for (const {action} of perAction) {
|
||||
Keybind.enableBuiltinForAction(action.action);
|
||||
}
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.re-enable-built-in"
|
||||
>
|
||||
{i18n._(RE_ENABLE_BUILT_IN_SHORTCUT_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
) : null;
|
||||
const globalGroup = globalAction ? (
|
||||
<MenuGroup data-flx="user.keybinds-tab.open-row-menu.global-menu-group">
|
||||
<CheckboxItem
|
||||
@@ -109,57 +133,56 @@ export const DefaultShortcutRow: React.FC<{row: ShortcutRowModel; overriddenActi
|
||||
>
|
||||
{i18n._(SET_CUSTOM_SHORTCUT_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<HideIcon size={16} data-flx="user.keybinds-tab.open-row-menu.hide-icon--2" />}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
for (const {action} of perAction) {
|
||||
Keybind.addCustomKeybindForAction(action.action);
|
||||
}
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--4"
|
||||
>
|
||||
{i18n._(DISABLE_BUILT_IN_SHORTCUT_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
);
|
||||
}
|
||||
if (allHaveCustom && perAction.length === 1) {
|
||||
const {action, hasEnabledCustomBinding} = perAction[0];
|
||||
return (
|
||||
<MenuGroup data-flx="user.keybinds-tab.open-row-menu.menu-group--3">
|
||||
{hasEnabledCustomBinding ? (
|
||||
{allBuiltinDisabled ? null : (
|
||||
<MenuItem
|
||||
icon={<RetryIcon size={16} data-flx="user.keybinds-tab.open-row-menu.retry-icon" />}
|
||||
danger
|
||||
icon={<HideIcon size={16} data-flx="user.keybinds-tab.open-row-menu.hide-icon--2" />}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
Keybind.removeCustomKeybindsForAction(action.action);
|
||||
for (const {action} of perAction) {
|
||||
Keybind.disableBuiltinForAction(action.action);
|
||||
}
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--5"
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--4"
|
||||
>
|
||||
{i18n._(RESET_TO_BUILT_IN_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
) : (
|
||||
<MenuItem
|
||||
icon={<EditSimpleIcon size={16} data-flx="user.keybinds-tab.open-row-menu.edit-simple-icon--3" />}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
Keybind.addCustomKeybindForAction(action.action);
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--6"
|
||||
>
|
||||
{i18n._(ADD_ANOTHER_CUSTOM_SHORTCUT_DESCRIPTOR)}
|
||||
{i18n._(DISABLE_BUILT_IN_SHORTCUT_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
)}
|
||||
</MenuGroup>
|
||||
);
|
||||
}
|
||||
if (perAction.length === 1) {
|
||||
const {action} = perAction[0];
|
||||
return (
|
||||
<MenuGroup data-flx="user.keybinds-tab.open-row-menu.menu-group--3">
|
||||
<MenuItem
|
||||
icon={<RetryIcon size={16} data-flx="user.keybinds-tab.open-row-menu.retry-icon" />}
|
||||
danger
|
||||
onClick={() => {
|
||||
onClose();
|
||||
Keybind.removeCustomKeybindsForAction(action.action);
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--5"
|
||||
>
|
||||
{i18n._(RESET_TO_BUILT_IN_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon={<EditSimpleIcon size={16} data-flx="user.keybinds-tab.open-row-menu.edit-simple-icon--3" />}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
Keybind.addCustomKeybindForAction(action.action);
|
||||
}}
|
||||
data-flx="user.keybinds-tab.open-row-menu.menu-item.close--6"
|
||||
>
|
||||
{i18n._(ADD_ANOTHER_CUSTOM_SHORTCUT_DESCRIPTOR)}
|
||||
</MenuItem>
|
||||
</MenuGroup>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{perAction.map(({action, label, customBindings, hasEnabledCustomBinding}) => (
|
||||
{perAction.map(({action, label, hasCustomBinding}) => (
|
||||
<MenuGroup key={action.action} data-flx="user.keybinds-tab.open-row-menu.menu-group--4">
|
||||
{customBindings.length > 0 && hasEnabledCustomBinding ? (
|
||||
{hasCustomBinding ? (
|
||||
<MenuItem
|
||||
icon={<RetryIcon size={16} data-flx="user.keybinds-tab.open-row-menu.retry-icon--2" />}
|
||||
danger
|
||||
@@ -191,6 +214,7 @@ export const DefaultShortcutRow: React.FC<{row: ShortcutRowModel; overriddenActi
|
||||
return (
|
||||
<>
|
||||
{globalGroup}
|
||||
{reEnableGroup}
|
||||
{renderBody()}
|
||||
</>
|
||||
);
|
||||
@@ -213,11 +237,13 @@ export const DefaultShortcutRow: React.FC<{row: ShortcutRowModel; overriddenActi
|
||||
<DefaultShortcutChipList
|
||||
chips={chipsForDefaultEntry(i18n, a)}
|
||||
overridden={overriddenActions.has(a.action)}
|
||||
disabled={disabledActions.has(a.action)}
|
||||
data-flx="user.keybinds-tab.default-shortcut-row.default-shortcut-chip-list"
|
||||
/>
|
||||
<DefaultShortcutChipList
|
||||
chips={chipsForDefaultEntry(i18n, b)}
|
||||
overridden={overriddenActions.has(b.action)}
|
||||
disabled={disabledActions.has(b.action)}
|
||||
data-flx="user.keybinds-tab.default-shortcut-row.default-shortcut-chip-list--2"
|
||||
/>
|
||||
</div>
|
||||
@@ -250,6 +276,7 @@ export const DefaultShortcutRow: React.FC<{row: ShortcutRowModel; overriddenActi
|
||||
<DefaultShortcutChipList
|
||||
chips={chipsForDefaultEntry(i18n, entry)}
|
||||
overridden={overriddenActions.has(entry.action)}
|
||||
disabled={disabledActions.has(entry.action)}
|
||||
data-flx="user.keybinds-tab.default-shortcut-row.default-shortcut-chip-list--3"
|
||||
/>
|
||||
<button
|
||||
|
||||
Reference in new issue
Block a user