Skip to content

Commit

Permalink
Feat/custom hotkeys (#496)
Browse files Browse the repository at this point in the history
* feat: 2024-04-07 15:27:52 - custom hotkeys

* feat: 2024-04-07 15:52:13 - add custome compare shortcuts

* feat: 2024-04-09 10:17:23 - Modify the code according to the code review suggestions and optimize the experience.

---------

Co-authored-by: more.tai <more.tai@huolala.cn>
  • Loading branch information
slarkvan and more.tai committed Apr 9, 2024
1 parent 0a6be17 commit 5b87d21
Show file tree
Hide file tree
Showing 13 changed files with 521 additions and 64 deletions.
7 changes: 6 additions & 1 deletion enjoy/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@
"deleteRecording": "delete recording",
"deleteRecordingConfirmation": "Are you sure to delete this recording?",
"myRecordings": "recordings",
"noRecordingForThisSegmentYet": "No recordings for this segment yet. Press <kbd>R</kbd> to start recording.",
"noRecordingForThisSegmentYet": "No recordings for this segment yet. Press <kbd>{{key}}</kbd> to start recording.",
"lastYear": "last year",
"less": "less",
"more": "more",
Expand Down Expand Up @@ -522,6 +522,11 @@
"AiDictionary": "AI dictionary",
"AiTranslate": "AI translate",
"cambridgeDictionary": "Cambridge dictionary",
"customizeShortcuts": "Customize shortcuts",
"customizeShortcutsTip":"Press any sequence of keys to set a 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",
"following": "following",
"followers": "followers",
"allUsers": "all users",
Expand Down
5 changes: 5 additions & 0 deletions enjoy/src/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,11 @@
"AiDictionary": "智能词典",
"AiTranslate": "智能翻译",
"cambridgeDictionary": "剑桥词典",
"customizeShortcuts": "自定义快捷键",
"customizeShortcutsTip":"按任意键序列设置快捷键",
"customizeShortcutsInvalidToast":"快捷键应最多含一个修饰键(Ctrl、Alt、Shift 或 Meta)和一个键,如 'Ctrl+C'",
"customizeShortcutsConflictToast": "{{input}}和已有{{otherHotkeyName}}的键位冲突了",
"customizeShortcutsUpdated": "设置成功",
"following": "关注中",
"followers": "被关注",
"allUsers": "所有用户",
Expand Down
11 changes: 10 additions & 1 deletion enjoy/src/main/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const libraryPath = () => {
settings.setSync(
"library",
process.env.LIBRARY_PATH ||
path.join(app.getPath("documents"), LIBRARY_PATH_SUFFIX)
path.join(app.getPath("documents"), LIBRARY_PATH_SUFFIX)
);
} else if (path.parse(_library).base !== LIBRARY_PATH_SUFFIX) {
settings.setSync("library", path.join(_library, LIBRARY_PATH_SUFFIX));
Expand Down Expand Up @@ -92,6 +92,7 @@ const userDataPath = () => {
return userData;
};


export default {
registerIpcHandlers: () => {
ipcMain.handle("settings-get-library", (_event) => {
Expand Down Expand Up @@ -152,6 +153,14 @@ export default {
ipcMain.handle("settings-set-default-engine", (_event, engine) => {
return settings.setSync("defaultEngine", engine);
});

ipcMain.handle("settings-get-default-hotkeys", (_event) => {
return settings.getSync("defaultHotkeys");
});

ipcMain.handle("settings-set-default-hotkeys", (_event, records) => {
return settings.setSync("defaultHotkeys", records);
});
},
cachePath,
libraryPath,
Expand Down
6 changes: 6 additions & 0 deletions enjoy/src/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,12 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
switchLanguage: (language: string) => {
return ipcRenderer.invoke("settings-switch-language", language);
},
getDefaultHotkeys: () => {
return ipcRenderer.invoke("settings-get-default-hotkeys");
},
setDefaultHotkeys: (records: Record<string, string>) => {
return ipcRenderer.invoke("settings-set-default-hotkeys", records);
},
},
path: {
join: (...paths: string[]) => {
Expand Down
34 changes: 10 additions & 24 deletions enjoy/src/renderer/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import {
AISettingsProvider,
AppSettingsProvider,
DbProvider,
HotKeysSettingsProvider,
} from "@renderer/context";
import router from "./router";
import { RouterProvider } from "react-router-dom";
import { Toaster, toast } from "@renderer/components/ui";
import { Tooltip } from "react-tooltip";
import { useHotkeys } from "react-hotkeys-hook";

function App() {
window.__ENJOY_APP__.onNotification((_event, notification) => {
Expand All @@ -31,32 +31,18 @@ function App() {
}
});

const ControlOrCommand = navigator.platform.includes("Mac")
? "Meta"
: "Control";

useHotkeys(`${ControlOrCommand}+Comma`, () => {
document.getElementById("preferences-button")?.click();
});

useHotkeys(`${ControlOrCommand}+Q`, () => {
window.__ENJOY_APP__.app.quit();
});

useHotkeys(`${ControlOrCommand}+Shift+I`, () => {
window.__ENJOY_APP__.app.openDevTools();
});

return (
<ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
<AppSettingsProvider>
<AISettingsProvider>
<DbProvider>
<RouterProvider router={router} />
<Toaster richColors position="top-center" />
<Tooltip id="global-tooltip" />
</DbProvider>
</AISettingsProvider>
<HotKeysSettingsProvider>
<AISettingsProvider>
<DbProvider>
<RouterProvider router={router} />
<Toaster richColors position="top-center" />
<Tooltip id="global-tooltip" />
</DbProvider>
</AISettingsProvider>
</HotKeysSettingsProvider>
</AppSettingsProvider>
</ThemeProvider>
);
Expand Down
103 changes: 103 additions & 0 deletions enjoy/src/renderer/components/change-hotkey-dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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 (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{t("customizeShortcuts")}</AlertDialogTitle>
</AlertDialogHeader>
<div>
<p className="pb-4">{name}</p>
<div className="flex items-center">
<p className="inline-block gap-2 border-2 border-black rounded p-[2px] mr-2">
{currentHotkeys[keyName]}
</p>
{joinedKeys.length > 0 ? (
<div className="flex items-center gap-1">
<p className="border-2 border-black rounded p-[2px]">
{joinedKeys}
</p>
<div className="cursor-pointer" onClick={changeKeyMap}>
<CheckIcon className="text-green-500 w-5 h-5" />
</div>
<div className="cursor-pointer" onClick={clear}>
<XIcon className="text-red-500 w-5 h-5" />
</div>
</div>
) : (
<div className="inline-block gap-2 border-2 border-black rounded p-[2px]">
<span className="text-sm">{t("customizeShortcutsTip")}</span>
<KeyboardIcon className="inline ml-1 w-6 h-6 text-muted-foreground" />
</div>
)}
</div>
</div>
<AlertDialogFooter>
<AlertDialogAction>Close</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};
34 changes: 21 additions & 13 deletions enjoy/src/renderer/components/medias/media-current-recording.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect, useContext, useRef, useState } from "react";
import {
AppSettingsProviderContext,
HotKeysSettingsProviderContext,
MediaPlayerProviderContext,
} from "@renderer/context";
import { MediaRecorder, RecordingDetail } from "@renderer/components";
Expand Down Expand Up @@ -60,6 +61,9 @@ export const MediaCurrentRecording = () => {
currentTime: mediaCurrentTime,
} = useContext(MediaPlayerProviderContext);
const { webApi, EnjoyApp } = useContext(AppSettingsProviderContext);
const { enabled, currentHotkeys } = useContext(
HotKeysSettingsProviderContext
);
const [player, setPlayer] = useState(null);
const [regions, setRegions] = useState<Regions | null>(null);
const [currentTime, setCurrentTime] = useState(0);
Expand Down Expand Up @@ -328,7 +332,7 @@ export const MediaCurrentRecording = () => {
}

const subscriptions = [
regions.on("region-created", () => { }),
regions.on("region-created", () => {}),

regions.on("region-clicked", (region, e) => {
e.stopPropagation();
Expand Down Expand Up @@ -399,7 +403,7 @@ export const MediaCurrentRecording = () => {
}, [currentRecording, isRecording, layout?.width]);

useHotkeys(
["Ctrl+R", "Meta+R"],
currentHotkeys.PlayOrPauseRecording,
(keyboardEvent, hotkeyEvent) => {
if (!player) return;
keyboardEvent.preventDefault();
Expand All @@ -411,6 +415,7 @@ export const MediaCurrentRecording = () => {
document.getElementById("recording-play-or-pause-button").click();
}
},
{ enabled },
[player]
);

Expand All @@ -422,7 +427,9 @@ export const MediaCurrentRecording = () => {
<div
className="m-auto"
dangerouslySetInnerHTML={{
__html: t("noRecordingForThisSegmentYet"),
__html: t("noRecordingForThisSegmentYet", {
key: currentHotkeys.StartOrStopRecording?.toUpperCase(),
}),
}}
></div>
</div>
Expand Down Expand Up @@ -551,16 +558,17 @@ export const MediaCurrentRecording = () => {
>
<GaugeCircleIcon
className={`w-4 h-4 mr-4
${currentRecording.pronunciationAssessment
? currentRecording.pronunciationAssessment
.pronunciationScore >= 80
? "text-green-500"
: currentRecording.pronunciationAssessment
.pronunciationScore >= 60
? "text-yellow-600"
: "text-red-500"
: ""
}
${
currentRecording.pronunciationAssessment
? currentRecording.pronunciationAssessment
.pronunciationScore >= 80
? "text-green-500"
: currentRecording.pronunciationAssessment
.pronunciationScore >= 60
? "text-yellow-600"
: "text-red-500"
: ""
}
`}
/>
<span>{t("pronunciationAssessment")}</span>
Expand Down
18 changes: 11 additions & 7 deletions enjoy/src/renderer/components/medias/media-player-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {
MediaPlayerProviderContext,
AppSettingsProviderContext,
HotKeysSettingsProviderContext,
} from "@renderer/context";
import {
ScissorsIcon,
Expand Down Expand Up @@ -56,6 +57,7 @@ export const MediaPlayerControls = () => {
setTranscriptionDraft,
} = useContext(MediaPlayerProviderContext);
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const { currentHotkeys, enabled } = useContext(HotKeysSettingsProviderContext)
const [playMode, setPlayMode] = useState<"loop" | "single" | "all">("single");
const [playbackRate, setPlaybackRate] = useState<number>(1);
const [grouping, setGrouping] = useState(false);
Expand Down Expand Up @@ -372,30 +374,32 @@ export const MediaPlayerControls = () => {
}, [wavesurfer, decoded, playMode, activeRegion, currentTime]);

useHotkeys(
["Space", "p", "n", "r", "c"],
[currentHotkeys.PlayOrPause, currentHotkeys.PlayPreviousSegment, currentHotkeys.PlayNextSegment, currentHotkeys.StartOrStopRecording, currentHotkeys.Compare],
(keyboardEvent, hotkeyEvent) => {
if (!wavesurfer) return;
keyboardEvent.preventDefault();

switch (hotkeyEvent.keys.join("")) {
case "space":
case currentHotkeys.PlayOrPause.toLowerCase():
document.getElementById("media-play-or-pause-button").click();
break;
case "p":
case currentHotkeys.PlayPreviousSegment.toLowerCase():
document.getElementById("media-play-previous-button").click();
break;
case "n":
case currentHotkeys.PlayNextSegment.toLowerCase():
document.getElementById("media-play-next-button").click();
break;
case "r":
case currentHotkeys.StartOrStopRecording.toLowerCase():
document.getElementById("media-record-button").click();
break;
case "c":
case currentHotkeys.Compare.toLowerCase():
document.getElementById("media-compare-button").click();
break;
}
},{
enabled
},
[wavesurfer]
[wavesurfer, currentHotkeys]
);

/*
Expand Down
Loading

0 comments on commit 5b87d21

Please sign in to comment.