diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json
index c6ff64d5..85944cec 100644
--- a/enjoy/src/i18n/en.json
+++ b/enjoy/src/i18n/en.json
@@ -296,7 +296,7 @@
"releaseToStop": "Release to stop",
"deleteRecording": "delete recording",
"deleteRecordingConfirmation": "Are you sure to delete this recording?",
- "myRecordings": "my recordings",
+ "myRecordings": "recordings",
"noRecordingForThisSegmentYet": "No recordings for this segment yet. Press R to start recording.",
"lastYear": "last year",
"less": "less",
diff --git a/enjoy/src/main/window.ts b/enjoy/src/main/window.ts
index 0a923afd..9ffffbb8 100644
--- a/enjoy/src/main/window.ts
+++ b/enjoy/src/main/window.ts
@@ -440,16 +440,20 @@ ${log}
// Create the browser window.
const mainWindow = new BrowserWindow({
icon: "./assets/icon.png",
- width: 1920,
- height: 1080,
- minWidth: 1440,
- minHeight: 900,
+ width: 1440,
+ height: 900,
+ minWidth: 1024,
+ minHeight: 768,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
spellcheck: false,
},
});
+ mainWindow.on("resize", () => {
+ mainWindow.webContents.send("window-on-resize", mainWindow.getBounds());
+ });
+
mainWindow.webContents.setWindowOpenHandler(() => {
return { action: "allow" };
});
diff --git a/enjoy/src/preload.ts b/enjoy/src/preload.ts
index d1f4a9d3..71275da5 100644
--- a/enjoy/src/preload.ts
+++ b/enjoy/src/preload.ts
@@ -34,6 +34,17 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", {
},
version,
},
+ window: {
+ onResize: (
+ callback: (
+ event: IpcRendererEvent,
+ bounds: { x: number; y: number; width: number; height: number }
+ ) => void
+ ) => ipcRenderer.on("window-on-resize", callback),
+ removeListeners: () => {
+ ipcRenderer.removeAllListeners("window-on-resize");
+ },
+ },
system: {
preferences: {
mediaAccess: (mediaType: "microphone" | "camera") => {
diff --git a/enjoy/src/renderer/components/audios/audio-player.tsx b/enjoy/src/renderer/components/audios/audio-player.tsx
index e8378323..db2a4935 100644
--- a/enjoy/src/renderer/components/audios/audio-player.tsx
+++ b/enjoy/src/renderer/components/audios/audio-player.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useContext, useRef } from "react";
+import { useEffect, useContext, useState } from "react";
import { MediaPlayerProviderContext } from "@renderer/context";
import {
MediaLoadingModal,
@@ -7,43 +7,45 @@ import {
MediaTabs,
MediaCurrentRecording,
MediaPlayer,
+ LoaderSpin,
} from "@renderer/components";
import { useAudio } from "@renderer/hooks";
export const AudioPlayer = (props: { id?: string; md5?: string }) => {
const { id, md5 } = props;
- const { setMedia } = useContext(MediaPlayerProviderContext);
+ const { setMedia, layout } = useContext(MediaPlayerProviderContext);
const { audio } = useAudio({ id, md5 });
useEffect(() => {
if (!audio) return;
-
setMedia(audio);
}, [audio]);
+ if (!layout) return ;
+
return (
-
-
-
+
+
-
-
-
+
+
+
-
+
-
diff --git a/enjoy/src/renderer/components/medias/media-current-recording.tsx b/enjoy/src/renderer/components/medias/media-current-recording.tsx
index 3a2a7316..75ee6f46 100644
--- a/enjoy/src/renderer/components/medias/media-current-recording.tsx
+++ b/enjoy/src/renderer/components/medias/media-current-recording.tsx
@@ -46,8 +46,9 @@ import { formatDuration } from "@renderer/lib/utils";
import { useHotkeys } from "react-hotkeys-hook";
export const MediaCurrentRecording = (props: { height?: number }) => {
- const { height = 192 } = props;
+ const { height } = props;
const {
+ layout,
isRecording,
setIsRecording,
currentRecording,
@@ -281,9 +282,9 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
});
return () => {
- ws.destroy();
+ ws?.destroy();
};
- }, [ref, currentRecording, isRecording]);
+ }, [ref, currentRecording, isRecording, height]);
useEffect(() => {
setIsComparing(false);
@@ -315,7 +316,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
}
const subscriptions = [
- regions.on("region-created", () => {}),
+ regions.on("region-created", () => { }),
regions.on("region-clicked", (region, e) => {
e.stopPropagation();
@@ -383,16 +384,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
useEffect(() => {
calContainerWidth();
- window.addEventListener("resize", () => {
- calContainerWidth();
- });
-
- return () => {
- window.removeEventListener("resize", () => {
- calContainerWidth();
- });
- };
- }, [currentRecording, isRecording]);
+ }, [currentRecording, isRecording, layout?.width]);
useHotkeys(
["Ctrl+R", "Meta+R"],
@@ -410,7 +402,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
[player]
);
- if (isRecording) return
;
+ if (isRecording) return
;
if (!currentRecording?.src)
return (
@@ -480,27 +472,31 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
setIsRecording={setIsRecording}
/>
-
+ {
+ height >= 192 && <>
+
-
+
+ >
+ }
@@ -522,17 +518,16 @@ export const MediaCurrentRecording = (props: { height?: number }) => {
>
= 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"
+ : ""
+ }
`}
/>
{t("pronunciationAssessment")}
diff --git a/enjoy/src/renderer/components/medias/media-player-controls.tsx b/enjoy/src/renderer/components/medias/media-player-controls.tsx
index d1182fb9..fedd0629 100644
--- a/enjoy/src/renderer/components/medias/media-player-controls.tsx
+++ b/enjoy/src/renderer/components/medias/media-player-controls.tsx
@@ -456,7 +456,7 @@ export const MediaPlayerControls = () => {
}, [grouping]);
return (
-
+
@@ -480,11 +480,10 @@ export const MediaPlayerControls = () => {
{PLAYBACK_RATE_OPTIONS.map((rate, i) => (
{
setPlaybackRate(rate);
}}
diff --git a/enjoy/src/renderer/components/medias/media-player.tsx b/enjoy/src/renderer/components/medias/media-player.tsx
index d46134db..8a2734a1 100644
--- a/enjoy/src/renderer/components/medias/media-player.tsx
+++ b/enjoy/src/renderer/components/medias/media-player.tsx
@@ -41,6 +41,7 @@ const MAX_ZOOM_RATIO = 4.0;
export const MediaPlayer = () => {
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
const {
+ layout,
media,
currentTime,
setRef,
@@ -132,16 +133,7 @@ export const MediaPlayer = () => {
useEffect(() => {
calContainerWidth();
- window.addEventListener("resize", () => {
- calContainerWidth();
- });
-
- return () => {
- window.removeEventListener("resize", () => {
- calContainerWidth();
- });
- };
- }, []);
+ }, [layout.width]);
return (
@@ -192,38 +184,44 @@ export const MediaPlayer = () => {
-
+ {
+ layout.name === "lg" && (
+ <>
+
-
+
+ >
+ )
+ }
@@ -239,6 +237,40 @@ export const MediaPlayer = () => {
+ {
+ layout.name === "sm" && (
+ <>
+ {
+ if (zoomRatio > MIN_ZOOM_RATIO) {
+ const nextZoomRatio = ZOOM_RATIO_OPTIONS.reverse().find(
+ (rate) => rate < zoomRatio
+ );
+ setZoomRatio(nextZoomRatio || MIN_ZOOM_RATIO);
+ }
+ }}
+ >
+
+ {t("zoomOut")}
+
+
+ {
+ setDisplayInlineCaption(!displayInlineCaption);
+ if (pitchChart) {
+ pitchChart.options.scales.x.display = !displayInlineCaption;
+ pitchChart.update();
+ }
+ }}
+ >
+
+ {t("inlineCaption")}
+
+ >
+ )
+ }
{
diff --git a/enjoy/src/renderer/components/medias/media-provider.tsx b/enjoy/src/renderer/components/medias/media-provider.tsx
index 7e2de34e..98144b2d 100644
--- a/enjoy/src/renderer/components/medias/media-provider.tsx
+++ b/enjoy/src/renderer/components/medias/media-provider.tsx
@@ -20,8 +20,9 @@ export const MediaProvider = () => {
if (!media?.src) return null;
return (
-
+
{
diff --git a/enjoy/src/renderer/components/medias/media-tabs.tsx b/enjoy/src/renderer/components/medias/media-tabs.tsx
index bfd8131c..617315fa 100644
--- a/enjoy/src/renderer/components/medias/media-tabs.tsx
+++ b/enjoy/src/renderer/components/medias/media-tabs.tsx
@@ -24,15 +24,13 @@ export const MediaTabs = () => {
return (
{media.mediaType === "Video" && (
setTab("provider")}
>
{t("player")}
@@ -40,25 +38,22 @@ export const MediaTabs = () => {
)}
setTab("transcription")}
>
{t("transcription")}
setTab("recordings")}
>
{t("myRecordings")}
setTab("info")}
>
{t("mediaInfo")}
diff --git a/enjoy/src/renderer/components/videos/video-player.tsx b/enjoy/src/renderer/components/videos/video-player.tsx
index 0c7d1da4..153e61c4 100644
--- a/enjoy/src/renderer/components/videos/video-player.tsx
+++ b/enjoy/src/renderer/components/videos/video-player.tsx
@@ -7,12 +7,13 @@ import {
MediaTabs,
MediaCurrentRecording,
MediaPlayer,
+ LoaderSpin,
} from "@renderer/components";
import { useVideo } from "@renderer/hooks";
export const VideoPlayer = (props: { id?: string; md5?: string }) => {
const { id, md5 } = props;
- const { setMedia } = useContext(MediaPlayerProviderContext);
+ const { setMedia, layout } = useContext(MediaPlayerProviderContext);
const { video } = useVideo({ id, md5 });
useEffect(() => {
@@ -21,29 +22,31 @@ export const VideoPlayer = (props: { id?: string; md5?: string }) => {
setMedia(video);
}, [video]);
+ if (!layout) return
;
+
return (
-
-
-
+
+
-
-
-
+
+
+
-
+
-
diff --git a/enjoy/src/renderer/context/media-player-provider.tsx b/enjoy/src/renderer/context/media-player-provider.tsx
index 3afd4931..e9deaee3 100644
--- a/enjoy/src/renderer/context/media-player-provider.tsx
+++ b/enjoy/src/renderer/context/media-player-provider.tsx
@@ -11,8 +11,10 @@ import { TimelineEntry } from "echogarden/dist/utilities/Timeline.d.js";
import { IPA_MAPPING } from "@/constants";
import { toast } from "@renderer/components/ui";
import { Tooltip } from "react-tooltip";
+import { debounce } from "lodash";
type MediaPlayerContextType = {
+ layout: { name: string, width: number, height: number, upperWrapper: string, lowerWrapper: string, playerWrapper: string, panelWrapper: string, playerHeight: number };
media: AudioType | VideoType;
setMedia: (media: AudioType | VideoType) => void;
setMediaProvider: (mediaProvider: HTMLAudioElement | null) => void;
@@ -68,15 +70,35 @@ type MediaPlayerContextType = {
export const MediaPlayerProviderContext =
createContext
(null);
+const LAYOUT = {
+ sm: {
+ name: 'sm',
+ upperWrapper: "h-[calc(100vh-27.5rem)]",
+ lowerWrapper: "h-[23rem]",
+ playerWrapper: "h-[9rem] mb-2",
+ panelWrapper: "h-16",
+ playerHeight: 128,
+ },
+ lg: {
+ name: 'lg',
+ upperWrapper: "h-[calc(100vh-37.5rem)]",
+ lowerWrapper: "h-[33rem]",
+ panelWrapper: "h-20",
+ playerWrapper: "h-[13rem] mb-4",
+ playerHeight: 192,
+ },
+}
+
export const MediaPlayerProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
- const height = 192;
const minPxPerSec = 150;
const { EnjoyApp } = useContext(AppSettingsProviderContext);
+ const [layout, setLayout] = useState<{ name: string, width: number, height: number, upperWrapper: string, lowerWrapper: string, playerWrapper: string, panelWrapper: string, playerHeight: number }>();
+
const [media, setMedia] = useState(null);
const [mediaProvider, setMediaProvider] = useState(
null
@@ -126,7 +148,7 @@ export const MediaPlayerProvider = ({
const ws = WaveSurfer.create({
container: ref.current,
- height,
+ height: layout.playerHeight,
waveColor: "#eaeaea",
progressColor: "#c0d6df",
cursorColor: "#ff0054",
@@ -196,7 +218,7 @@ export const MediaPlayerProvider = ({
const canvasId = options?.canvasId || `pitch-contour-${region.id}-canvas`;
canvas.id = canvasId;
canvas.style.width = `${width}px`;
- canvas.style.height = `${height}px`;
+ canvas.style.height = `${layout.playerHeight}px`;
pitchContourWidthContainer.appendChild(canvas);
pitchContourWidthContainer.style.position = "absolute";
@@ -204,7 +226,7 @@ export const MediaPlayerProvider = ({
pitchContourWidthContainer.style.left = "0";
pitchContourWidthContainer.style.width = `${width}px`;
- pitchContourWidthContainer.style.height = `${height}px`;
+ pitchContourWidthContainer.style.height = `${layout.playerHeight}px`;
pitchContourWidthContainer.style.marginLeft = `${offsetLeft}px`;
pitchContourWidthContainer.classList.add(
"pitch-contour",
@@ -317,6 +339,16 @@ export const MediaPlayerProvider = ({
);
};
+ const calculateHeight = () => {
+ if (window.innerHeight <= 1080) {
+ setLayout({ ...LAYOUT.sm, width: window.innerWidth, height: window.innerHeight });
+ } else {
+ setLayout({ ...LAYOUT.lg, width: window.innerWidth, height: window.innerHeight });
+ }
+ }
+
+ const deboundeCalculateHeight = debounce(calculateHeight, 100);
+
/*
* When wavesurfer is decoded,
* set up event listeners for wavesurfer
@@ -432,15 +464,37 @@ export const MediaPlayerProvider = ({
* and mediaProvider is available
*/
useEffect(() => {
+ if (!layout?.playerHeight) return;
+ if (!media) return;
+ if (!ref) return;
+ if (!mediaProvider) return;
+
initializeWavesurfer();
setDecoded(false);
setDecodeError(null);
- }, [media, ref, mediaProvider]);
+
+ return () => {
+ if (wavesurfer) wavesurfer.destroy();
+ }
+ }, [media, ref, mediaProvider, layout?.playerHeight]);
+
+ useEffect(() => {
+ calculateHeight();
+
+ EnjoyApp.window.onResize((event, bounds) => {
+ deboundeCalculateHeight();
+ })
+
+ return () => {
+ EnjoyApp.window.removeListeners();
+ }
+ }, [])
return (
<>
Promise;
version: string;
};
+ window: {
+ onResize: (callback: (event, bounds: any) => void) => void;
+ removeListeners: () => void;
+ };
system: {
preferences: {
mediaAccess: (mediaType: "microphone") => Promise;