From 09544caccc8a94d50a296cdcc97b41d67144a3b2 Mon Sep 17 00:00:00 2001
From: James Pike
Date: Thu, 10 Oct 2024 21:21:56 +0800
Subject: [PATCH] Allow customising certain keyboard shortcuts
This allows customising next/previous service shortcuts, but opens the
door to using the same code and UI for customizing further shortcuts.
These particular shortcuts were requested to be customizable in the
github issue https://github.com/ferdium/ferdium-app/issues/53
---
.../settings/settings/EditSettingsForm.tsx | 26 ++++++++++++++++
src/config.ts | 9 +++++-
.../settings/EditSettingsScreen.tsx | 31 +++++++++++++++++++
src/i18n/locales/en-US.json | 1 +
src/index.ts | 8 ++++-
src/lib/Menu.ts | 6 ++--
src/stores/SettingsStore.ts | 7 +++++
7 files changed, 83 insertions(+), 5 deletions(-)
diff --git a/src/components/settings/settings/EditSettingsForm.tsx b/src/components/settings/settings/EditSettingsForm.tsx
index 2900aa2af6..3d75d7a59b 100644
--- a/src/components/settings/settings/EditSettingsForm.tsx
+++ b/src/components/settings/settings/EditSettingsForm.tsx
@@ -200,6 +200,10 @@ const messages = defineMessages({
id: 'settings.app.subheadlineDownloads',
defaultMessage: 'Downloads',
},
+ subheadlineShortcuts: {
+ id: 'settings.app.subheadlineShortcuts',
+ defaultMessage: 'Shortcuts',
+ },
cacheInfo: {
id: 'settings.app.cacheInfo',
defaultMessage: 'Ferdium cache is currently using {size} of disk space.',
@@ -1172,6 +1176,28 @@ class EditSettingsForm extends Component {
})}
+
+
+
+
+
{intl.formatMessage(messages.subheadlineShortcuts)}
+
+
this.submit(e)}
+ {...form.$('shortcutActivateNextService').bind()}
+ />
+
+
this.submit(e)}
+ {...form.$('shortcutActivatePreviousService').bind()}
+ />
+
+
+ {intl.formatMessage(messages.appRestartRequired)}
+
+
)}
diff --git a/src/config.ts b/src/config.ts
index 0034b00a65..ec0519235a 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -2,6 +2,8 @@
import ms from 'ms';
+import { cmdOrCtrlShortcutKey, shiftKey } from './environment';
+
export const DEFAULT_ACCENT_COLOR = '#7367F0';
export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks
@@ -326,7 +328,7 @@ export const FERDIUM_TRANSLATION = 'https://crowdin.com/project/ferdium-app';
export const FERDIUM_DEV_DOCS =
'https://github.com/ferdium/ferdium-recipes/blob/main/docs/integration.md';
-export const FILE_SYSTEM_SETTINGS_TYPES = ['app', 'proxy'];
+export const FILE_SYSTEM_SETTINGS_TYPES = ['app', 'proxy', 'shortcuts'];
export const LOCAL_SERVER = 'You are using Ferdium without a server';
export const SERVER_NOT_LOADED = 'Ferdium::SERVER_NOT_LOADED';
@@ -469,3 +471,8 @@ export const DEFAULT_SERVICE_SETTINGS = {
darkReaderContrast: 90,
darkReaderSepia: 10,
};
+
+export const DEFAULT_SHORTCUTS = {
+ activateNextService: `${cmdOrCtrlShortcutKey()}+tab`,
+ activatePreviousService: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`,
+};
diff --git a/src/containers/settings/EditSettingsScreen.tsx b/src/containers/settings/EditSettingsScreen.tsx
index 31e8d64840..d765955395 100644
--- a/src/containers/settings/EditSettingsScreen.tsx
+++ b/src/containers/settings/EditSettingsScreen.tsx
@@ -11,6 +11,7 @@ import type { StoresProps } from '../../@types/ferdium-components.types';
import type { FormFields } from '../../@types/mobx-form.types';
import {
DEFAULT_APP_SETTINGS,
+ DEFAULT_SHORTCUTS,
GOOGLE_TRANSLATOR_LANGUAGES,
HIBERNATION_STRATEGIES,
ICON_SIZES,
@@ -42,6 +43,7 @@ import ErrorBoundary from '../../components/util/ErrorBoundary';
import { importExportURL } from '../../api/apiBase';
import globalMessages from '../../i18n/globalMessages';
import { ifUndefined } from '../../jsUtils';
+import { menuItems } from '../../lib/Menu';
const debug = require('../../preload-safe-debug')('Ferdium:EditSettingsScreen');
@@ -499,6 +501,11 @@ class EditSettingsScreen extends Component<
locale: settingsData.locale, // we need this info in the main process as well
};
+ const newShortcuts = {
+ activateNextService: settingsData.shortcutActivateNextService,
+ activatePreviousService: settingsData.shortcutActivatePreviousService,
+ };
+
const requiredRestartKeys = [
'webRTCIPHandlingPolicy',
'sentry',
@@ -539,6 +546,12 @@ class EditSettingsScreen extends Component<
data: newSettings,
});
+ settings.update({
+ type: 'shortcuts',
+ // TODO: The conversions might not be necessary once we convert to typescript
+ data: newShortcuts,
+ });
+
user.update({
userData: {
automaticUpdates: Boolean(settingsData.automaticUpdates),
@@ -1297,6 +1310,24 @@ class EditSettingsScreen extends Component<
default: DEFAULT_APP_SETTINGS.automaticUpdates,
type: 'checkbox',
},
+ shortcutActivateNextService: {
+ label: intl.formatMessage(menuItems.activateNextService),
+ value: ifUndefined(
+ settings.all.shortcuts.activateNextService,
+ DEFAULT_SHORTCUTS.activateNextService,
+ ),
+ default: DEFAULT_SHORTCUTS.activateNextService,
+ placeholder: DEFAULT_SHORTCUTS.activateNextService,
+ },
+ shortcutActivatePreviousService: {
+ label: intl.formatMessage(menuItems.activatePreviousService),
+ value: ifUndefined(
+ settings.all.shortcuts.activatePreviousService,
+ DEFAULT_SHORTCUTS.activatePreviousService,
+ ),
+ default: DEFAULT_SHORTCUTS.activatePreviousService,
+ placeholder: DEFAULT_SHORTCUTS.activatePreviousService,
+ },
},
};
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 9ea73cf2a7..69067897d2 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -317,6 +317,7 @@
"settings.app.subheadlineCache": "Cache",
"settings.app.subheadlineDownloads": "Downloads",
"settings.app.subheadlineFerdiumProfile": "Ferdium Profile",
+ "settings.app.subheadlineShortcuts": "Shortcuts",
"settings.app.subheadlineUserAgent": "User Agent",
"settings.app.todoServerInfo": "This server will be used for the \"Ferdium Todo\" feature.",
"settings.app.translationHelp": "Help us to translate Ferdium into your language.",
diff --git a/src/index.ts b/src/index.ts
index 286b305c28..72928e2bab 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -22,7 +22,11 @@ import enforceMacOSAppLocation from './enforce-macos-app-location';
initializeRemote();
-import { DEFAULT_APP_SETTINGS, DEFAULT_WINDOW_OPTIONS } from './config';
+import {
+ DEFAULT_APP_SETTINGS,
+ DEFAULT_SHORTCUTS,
+ DEFAULT_WINDOW_OPTIONS,
+} from './config';
import { altKey, isLinux, isMac, isWindows } from './environment';
import {
@@ -95,6 +99,7 @@ if (isWindows) {
// Initialize Settings
const settings = new Settings('app', DEFAULT_APP_SETTINGS);
const proxySettings = new Settings('proxy');
+const shortcutSettings = new Settings('shortcuts', DEFAULT_SHORTCUTS);
const retrieveSettingValue = (key: string, defaultValue: boolean | string) =>
ifUndefined(settings.get(key), defaultValue);
@@ -283,6 +288,7 @@ const createWindow = () => {
settings: {
app: settings,
proxy: proxySettings,
+ shortcuts: shortcutSettings,
},
trayIcon,
});
diff --git a/src/lib/Menu.ts b/src/lib/Menu.ts
index 05a456ac50..01ecf4320f 100644
--- a/src/lib/Menu.ts
+++ b/src/lib/Menu.ts
@@ -51,7 +51,7 @@ import { acceleratorString } from '../jsUtils';
import type Service from '../models/Service';
import type { RealStores } from '../stores';
-const menuItems = defineMessages({
+export const menuItems = defineMessages({
edit: {
id: 'menu.edit',
defaultMessage: 'Edit',
@@ -1102,7 +1102,7 @@ class FranzMenu implements StoresProps {
},
{
label: intl.formatMessage(menuItems.activateNextService),
- accelerator: `${cmdOrCtrlShortcutKey()}+tab`,
+ accelerator: this.stores.settings.shortcuts.activateNextService,
click: () => this.actions.service.setActiveNext(),
visible: !cmdAltShortcutsVisibile,
},
@@ -1114,7 +1114,7 @@ class FranzMenu implements StoresProps {
},
{
label: intl.formatMessage(menuItems.activatePreviousService),
- accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`,
+ accelerator: this.stores.settings.shortcuts.activatePreviousService,
click: () => this.actions.service.setActivePrev(),
visible: !cmdAltShortcutsVisibile,
},
diff --git a/src/stores/SettingsStore.ts b/src/stores/SettingsStore.ts
index 29f173f910..7e92dd8cff 100644
--- a/src/stores/SettingsStore.ts
+++ b/src/stores/SettingsStore.ts
@@ -7,6 +7,7 @@ import type { Actions } from '../actions/lib/actions';
import type { ApiInterface } from '../api';
import {
DEFAULT_APP_SETTINGS,
+ DEFAULT_SHORTCUTS,
FILE_SYSTEM_SETTINGS_TYPES,
LOCAL_SERVER,
} from '../config';
@@ -23,6 +24,7 @@ export default class SettingsStore extends TypedStore {
@observable _fileSystemSettingsCache = {
app: DEFAULT_APP_SETTINGS,
proxy: {},
+ shortcuts: DEFAULT_SHORTCUTS,
};
constructor(stores: Stores, api: ApiInterface, actions: Actions) {
@@ -126,6 +128,10 @@ export default class SettingsStore extends TypedStore {
);
}
+ @computed get shortcuts() {
+ return this._fileSystemSettingsCache.shortcuts || DEFAULT_SHORTCUTS;
+ }
+
@computed get stats() {
return (
localStorage.getItem('stats') || {
@@ -145,6 +151,7 @@ export default class SettingsStore extends TypedStore {
service: this.service,
stats: this.stats,
migration: this.migration,
+ shortcuts: this.shortcuts,
};
}