From 7dfd47bb6d5facf3b8a8b3919120d7d8e8d0444f Mon Sep 17 00:00:00 2001 From: an-lee Date: Wed, 10 Apr 2024 11:54:28 +0800 Subject: [PATCH] Refactor hotkey setting UI (#506) * refactor hotkeys setting * update UI * refactor * fix toast --- enjoy/src/i18n/en.json | 3 +- enjoy/src/i18n/zh-CN.json | 7 +- .../components/change-hotkey-dialog.tsx | 103 -------------- .../preferences/hotkeys-settings.tsx | 129 ++++++++++++++++++ .../components/preferences/hotkeys.tsx | 46 +++---- .../renderer/components/preferences/index.ts | 2 + .../context/hotkeys-settings-provider.tsx | 13 +- 7 files changed, 163 insertions(+), 140 deletions(-) delete mode 100644 enjoy/src/renderer/components/change-hotkey-dialog.tsx create mode 100644 enjoy/src/renderer/components/preferences/hotkeys-settings.tsx diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json index 6a9b0d12..fd137187 100644 --- a/enjoy/src/i18n/en.json +++ b/enjoy/src/i18n/en.json @@ -523,7 +523,8 @@ "AiTranslate": "AI translate", "cambridgeDictionary": "Cambridge dictionary", "customizeShortcuts": "Customize shortcuts", - "customizeShortcutsTip":"Press any sequence of keys to set a shortcut", + "customizeShortcutsTip":"Click to change", + "customizeShortcutsRecordingTip":"Recording new shortcut", "customizeShortcutsInvalidToast": "Your shortcut should only have one modifier (Ctrl, Alt, Shift, or Meta) and one key, like 'Ctrl+C'.", "customizeShortcutsConflictToast": "{{input}} conflicts with the existing {{otherHotkeyName}} shortcut.", "customizeShortcutsUpdated": "Changes saved", diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json index 22a1c115..5e15d3d4 100644 --- a/enjoy/src/i18n/zh-CN.json +++ b/enjoy/src/i18n/zh-CN.json @@ -522,9 +522,10 @@ "AiTranslate": "智能翻译", "cambridgeDictionary": "剑桥词典", "customizeShortcuts": "自定义快捷键", - "customizeShortcutsTip":"按任意键序列设置快捷键", - "customizeShortcutsInvalidToast":"快捷键应最多含一个修饰键(Ctrl、Alt、Shift 或 Meta)和一个键,如 'Ctrl+C'", - "customizeShortcutsConflictToast": "{{input}}和已有{{otherHotkeyName}}的键位冲突了", + "customizeShortcutsTip":"点击重新录制", + "customizeShortcutsRecordingTip":"正在录制快捷键", + "customizeShortcutsInvalidToast":"快捷键应最多含一个修饰键(Ctrl, Alt, Shift 或 Meta)和一个键,如 'Ctrl+C'", + "customizeShortcutsConflictToast": "{{input}} 和已有 {{otherHotkeyName}} 的键位冲突了", "customizeShortcutsUpdated": "设置成功", "following": "关注中", "followers": "被关注", diff --git a/enjoy/src/renderer/components/change-hotkey-dialog.tsx b/enjoy/src/renderer/components/change-hotkey-dialog.tsx deleted file mode 100644 index d016f357..00000000 --- a/enjoy/src/renderer/components/change-hotkey-dialog.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { - AlertDialog, - AlertDialogAction, - AlertDialogContent, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from "@renderer/components/ui"; -import { toast } from "@renderer/components/ui"; -import { useContext, useMemo } from "react"; -import { HotKeysSettingsProviderContext } from "../context"; -import { CheckIcon, KeyboardIcon, XIcon } from "lucide-react"; -import { t } from "i18next"; - -export const ChangeHotkeyDialog = ({ - open, - name, - keyName, - onOpenChange, -}: { - open: boolean; - name: string; - keyName: string; - onOpenChange: (open: boolean) => void; -}) => { - const { - changeHotkey, - currentHotkeys, - recordingHotkeys, - resetRecordingHotkeys, - } = useContext(HotKeysSettingsProviderContext); - - const joinedKeys = useMemo(() => [...recordingHotkeys].join("+"), [ - recordingHotkeys, - ]); - - const changeKeyMap = async () => { - const ret = ((await changeHotkey( - keyName, - recordingHotkeys - )) as unknown) as { - error: "conflict" | "invalid"; - data: string | string[]; - input: string; - }; - const { error, data, input } = ret ?? {}; - if (error === "conflict") { - toast.error( - t("customizeShortcutsConflictToast", { - input, - otherHotkeyName: (data as string[]).join(","), - }) - ); - } else if (error === "invalid") { - toast.error(t("customizeShortcutsInvalidToast")); - } else { - toast.success(t("customizeShortcutsUpdated")); - } - }; - - const clear = () => { - resetRecordingHotkeys(); - }; - - return ( - - - - {t("customizeShortcuts")} - -
-

{name}

-
-

- {currentHotkeys[keyName]} -

- {joinedKeys.length > 0 ? ( -
-

- {joinedKeys} -

-
- -
-
- -
-
- ) : ( -
- {t("customizeShortcutsTip")} - -
- )} -
-
- - Close - -
-
- ); -}; diff --git a/enjoy/src/renderer/components/preferences/hotkeys-settings.tsx b/enjoy/src/renderer/components/preferences/hotkeys-settings.tsx new file mode 100644 index 00000000..4352c6ac --- /dev/null +++ b/enjoy/src/renderer/components/preferences/hotkeys-settings.tsx @@ -0,0 +1,129 @@ +import { t } from "i18next"; +import { + AlertDialog, + AlertDialogCancel, + AlertDialogContent, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + Button, + toast, +} from "@renderer/components/ui"; +import { HotKeysSettingsProviderContext } from "@renderer/context"; +import { useContext, useMemo, useEffect } from "react"; + +export const HotkeysSettings = ({ + open, + name, + keyName, + onOpenChange, +}: { + open: boolean; + name: string; + keyName: string; + onOpenChange: (open: boolean) => void; +}) => { + const { + changeHotkey, + currentHotkeys, + recordingHotkeys, + resetRecordingHotkeys, + startRecordingHotkeys, + stopRecordingHotkeys, + isRecording, + } = useContext(HotKeysSettingsProviderContext); + + const joinedKeys = useMemo( + () => [...recordingHotkeys].join("+"), + [recordingHotkeys] + ); + + const changeKeyMap = async () => { + const ret = (await changeHotkey(keyName, recordingHotkeys)) as unknown as { + error: "conflict" | "invalid"; + data: string | string[]; + input: string; + }; + stopRecordingHotkeys(); + const { error, data, input } = ret ?? {}; + + if (error === "conflict") { + toast.error( + t("customizeShortcutsConflictToast", { + input, + otherHotkeyName: (data as string[]) + .map((str) => t(str.charAt(0).toLowerCase() + str.slice(1))) + .join(","), + }) + ); + } else if (error === "invalid") { + toast.error(t("customizeShortcutsInvalidToast")); + } else { + toast.success(t("customizeShortcutsUpdated")); + onOpenChange(false); + } + }; + + const reset = () => { + stopRecordingHotkeys(); + resetRecordingHotkeys(); + }; + + // ensure recording disabled when dialog close + useEffect(() => { + return () => { + stopRecordingHotkeys(); + }; + }, [open]); + + return ( + + + + {name} + +
+ {isRecording ? ( +
+
+ +
+
+ {t("customizeShortcutsRecordingTip")} +
+
+ ) : ( +
+
+ +
+
+ {t("customizeShortcutsTip")} +
+
+ )} +
+ + + {t("cancel")} + +
+
+ ); +}; diff --git a/enjoy/src/renderer/components/preferences/hotkeys.tsx b/enjoy/src/renderer/components/preferences/hotkeys.tsx index 604d872a..1e997cf8 100644 --- a/enjoy/src/renderer/components/preferences/hotkeys.tsx +++ b/enjoy/src/renderer/components/preferences/hotkeys.tsx @@ -1,8 +1,10 @@ import { t } from "i18next"; -import { Separator } from "@renderer/components/ui"; -import { HotKeysSettingsProviderContext, Hotkey } from "@/renderer/context"; +import { + Separator, +} from "@renderer/components/ui"; +import { HotKeysSettingsProviderContext, Hotkey } from "@renderer/context"; +import { HotkeysSettings } from "@renderer/components"; import { useContext, useState } from "react"; -import { ChangeHotkeyDialog } from "../change-hotkey-dialog"; export const Hotkeys = () => { const [open, setOpen] = useState(false); @@ -10,27 +12,15 @@ export const Hotkeys = () => { name: string; keyName: string; } | null>(null); - const { - currentHotkeys, - startRecordingHotkeys, - stopRecordingHotkeys, - } = useContext(HotKeysSettingsProviderContext); + const { currentHotkeys } = useContext(HotKeysSettingsProviderContext); const commandOrCtrl = navigator.platform.includes("Mac") ? "Cmd" : "Ctrl"; const handleItemSelected = (item: { name: string; keyName: Hotkey }) => { setOpen(true); - startRecordingHotkeys(); setSelectedItem(item); }; - const handleOpenChange = (open: boolean) => { - setOpen(open); - if (!open) { - stopRecordingHotkeys(); - } - }; - return ( <>
{t("hotkeys")}
@@ -39,8 +29,8 @@ export const Hotkeys = () => {
{t("quitApp")}
- - {commandOrCtrl} + Q + + {commandOrCtrl}+Q
@@ -53,11 +43,11 @@ export const Hotkeys = () => { handleItemSelected({ - name: "Open preferences", + name: t("openPreferences"), keyName: "OpenPreferences", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.OpenPreferences} @@ -77,7 +67,7 @@ export const Hotkeys = () => { keyName: "PlayOrPause", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.PlayOrPause} @@ -96,7 +86,7 @@ export const Hotkeys = () => { keyName: "StartOrStopRecording", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.StartOrStopRecording} @@ -115,7 +105,7 @@ export const Hotkeys = () => { keyName: "PlayOrPauseRecording", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.PlayOrPauseRecording} @@ -153,7 +143,7 @@ export const Hotkeys = () => { keyName: "PlayNextSegment", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.PlayNextSegment} @@ -172,7 +162,7 @@ export const Hotkeys = () => { keyName: "Compare", }) } - className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer" + className="bg-muted px-2 py-1 rounded-md text-sm text-muted-foreground cursor-pointer capitalize" > {currentHotkeys.Compare} @@ -181,12 +171,12 @@ export const Hotkeys = () => { - ); -}; +}; \ No newline at end of file diff --git a/enjoy/src/renderer/components/preferences/index.ts b/enjoy/src/renderer/components/preferences/index.ts index 6ba224ee..4fa9c13b 100644 --- a/enjoy/src/renderer/components/preferences/index.ts +++ b/enjoy/src/renderer/components/preferences/index.ts @@ -1,6 +1,8 @@ export * from "./preferences"; export * from "./about"; + export * from "./hotkeys"; +export * from "./hotkeys-settings"; export * from "./default-engine-settings"; export * from "./openai-settings"; diff --git a/enjoy/src/renderer/context/hotkeys-settings-provider.tsx b/enjoy/src/renderer/context/hotkeys-settings-provider.tsx index 943b1687..9c664673 100644 --- a/enjoy/src/renderer/context/hotkeys-settings-provider.tsx +++ b/enjoy/src/renderer/context/hotkeys-settings-provider.tsx @@ -47,11 +47,11 @@ const defaultKeyMap = { OpenPreferences: `${ControlOrCommand}+Comma`, // player PlayOrPause: "Space", - StartOrStopRecording: "r", - PlayOrPauseRecording: `${ControlOrCommand}+r`, - PlayPreviousSegment: "p", - PlayNextSegment: "n", - Compare: "c", + StartOrStopRecording: "R", + PlayOrPauseRecording: `${ControlOrCommand}+R`, + PlayPreviousSegment: "P", + PlayNextSegment: "N", + Compare: "C", // dev tools OpenDevTools: `${ControlOrCommand}+Shift+I`, }; @@ -76,6 +76,7 @@ type HotkeysSettingsProviderState = { currentHotkeys: Record; recordingHotkeys?: any; enabled: boolean; + isRecording: boolean; startRecordingHotkeys?: () => void; stopRecordingHotkeys?: () => void; resetRecordingHotkeys?: () => void; @@ -85,6 +86,7 @@ type HotkeysSettingsProviderState = { const initialState: HotkeysSettingsProviderState = { currentHotkeys: {}, enabled: true, + isRecording: false, }; export const HotKeysSettingsProviderContext = createContext< @@ -215,6 +217,7 @@ export const HotKeysSettingsProvider = ({ currentHotkeys, recordingHotkeys: keys, enabled: !isRecording, + isRecording, startRecordingHotkeys, stopRecordingHotkeys, resetRecordingHotkeys: resetKeys,