diff --git a/src/App.jsx b/src/App.jsx
index b95b2368..6fbc1e9e 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,6 +1,6 @@
import React, { memo, useEffect, useState, useCallback, useRef, useMemo } from 'react';
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom';
-import { FaAngleLeft, FaWindowClose, FaTimes, FaAngleRight, FaFile } from 'react-icons/fa';
+import { FaAngleLeft, FaWindowClose, FaTimes } from 'react-icons/fa';
import { AnimatePresence, motion } from 'framer-motion';
import Lottie from 'react-lottie-player';
import { SideSheet, Button, Position, ForkIcon, DisableIcon, Select, ThemeProvider } from 'evergreen-ui';
@@ -39,6 +39,8 @@ import ExportConfirm from './ExportConfirm';
import ValueTuner from './components/ValueTuner';
import VolumeControl from './components/VolumeControl';
import SubtitleControl from './components/SubtitleControl';
+import BatchFile from './components/BatchFile';
+
import { loadMifiLink } from './mifi';
import { primaryColor, controlsBackground, timelineBackground } from './colors';
import allOutFormats from './outFormats';
@@ -2331,17 +2333,9 @@ const App = memo(() => {
- {batchFiles.map(({ path, name }) => {
- const isCurrent = path === filePath;
- return (
-
batchOpenSingleFile(path)}>
-
-
{name}
-
- {isCurrent &&
}
-
- );
- })}
+ {batchFiles.map(({ path, name }) => (
+
+ ))}
)}
diff --git a/src/SegmentList.jsx b/src/SegmentList.jsx
index 6400ea18..f13b843a 100644
--- a/src/SegmentList.jsx
+++ b/src/SegmentList.jsx
@@ -25,33 +25,38 @@ const Segment = memo(({ seg, index, currentSegIndex, formatTimecode, getFrameCou
const ref = useRef();
- useContextMenu(ref, invertCutSegments ? [] : [
- { label: t('Jump to cut start'), click: jumpSegStart },
- { label: t('Jump to cut end'), click: jumpSegEnd },
+ const contextMenuTemplate = useMemo(() => {
+ if (invertCutSegments) return [];
+ return [
+ { label: t('Jump to cut start'), click: jumpSegStart },
+ { label: t('Jump to cut end'), click: jumpSegEnd },
- { type: 'separator' },
+ { type: 'separator' },
- { label: t('Add segment'), click: addCutSegment },
- { label: t('Label segment'), click: onLabelPress },
- { label: t('Remove segment'), click: onRemovePress },
+ { label: t('Add segment'), click: addCutSegment },
+ { label: t('Label segment'), click: onLabelPress },
+ { label: t('Remove segment'), click: onRemovePress },
- { type: 'separator' },
+ { type: 'separator' },
- { label: t('Change segment order'), click: onReorderPress },
- { label: t('Increase segment order'), click: () => updateOrder(1) },
- { label: t('Decrease segment order'), click: () => updateOrder(-1) },
+ { label: t('Change segment order'), click: onReorderPress },
+ { label: t('Increase segment order'), click: () => updateOrder(1) },
+ { label: t('Decrease segment order'), click: () => updateOrder(-1) },
- { type: 'separator' },
+ { type: 'separator' },
- { label: t('Include ONLY this segment in export'), click: () => onExportSingleSegmentClick(seg) },
- { label: enabled ? t('Exclude this segment from export') : t('Include this segment in export'), click: () => onExportSegmentEnabledToggle(seg) },
- { label: t('Include all segments in export'), click: () => onExportSegmentEnableAll(seg) },
- { label: t('Exclude all segments from export'), click: () => onExportSegmentDisableAll(seg) },
+ { label: t('Include ONLY this segment in export'), click: () => onExportSingleSegmentClick(seg) },
+ { label: enabled ? t('Exclude this segment from export') : t('Include this segment in export'), click: () => onExportSegmentEnabledToggle(seg) },
+ { label: t('Include all segments in export'), click: () => onExportSegmentEnableAll(seg) },
+ { label: t('Exclude all segments from export'), click: () => onExportSegmentDisableAll(seg) },
- { type: 'separator' },
+ { type: 'separator' },
- { label: t('Segment tags'), click: () => onViewSegmentTagsPress(index) },
- ]);
+ { label: t('Segment tags'), click: () => onViewSegmentTagsPress(index) },
+ ];
+ }, [addCutSegment, enabled, index, invertCutSegments, jumpSegEnd, jumpSegStart, onExportSegmentDisableAll, onExportSegmentEnableAll, onExportSegmentEnabledToggle, onExportSingleSegmentClick, onLabelPress, onRemovePress, onReorderPress, onViewSegmentTagsPress, seg, t, updateOrder]);
+
+ useContextMenu(ref, contextMenuTemplate);
const duration = seg.end - seg.start;
const durationMs = duration * 1000;
diff --git a/src/Timeline.jsx b/src/Timeline.jsx
index e984fd0f..39e8c564 100644
--- a/src/Timeline.jsx
+++ b/src/Timeline.jsx
@@ -192,9 +192,11 @@ const Timeline = memo(({
const onMouseMove = useCallback((e) => setHoveringTime(getMouseTimelinePos(e.nativeEvent)), [getMouseTimelinePos]);
const onMouseOut = useCallback(() => setHoveringTime(), []);
- useContextMenu(timelineScrollerRef, [
+ const contextMenuTemplate = useMemo(() => [
{ label: t('Seek to timecode'), click: goToTimecode },
- ]);
+ ], [goToTimecode, t]);
+
+ useContextMenu(timelineScrollerRef, contextMenuTemplate);
return (
// eslint-disable-next-line jsx-a11y/mouse-events-have-key-events
diff --git a/src/components/BatchFile.jsx b/src/components/BatchFile.jsx
new file mode 100644
index 00000000..3c8b5e13
--- /dev/null
+++ b/src/components/BatchFile.jsx
@@ -0,0 +1,29 @@
+import React, { memo, useRef, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { FaAngleRight, FaFile } from 'react-icons/fa';
+
+import useContextMenu from '../hooks/useContextMenu';
+import { primaryColor } from '../colors';
+
+const BatchFile = memo(({ path, filePath, name, onOpen, onDelete }) => {
+ const ref = useRef();
+
+ const { t } = useTranslation();
+ const contextMenuTemplate = useMemo(() => [
+ { label: t('Remove'), click: () => onDelete(path) },
+ ], [t, onDelete, path]);
+
+ useContextMenu(ref, contextMenuTemplate);
+ const isCurrent = path === filePath;
+
+ return (
+ onOpen(path)}>
+
+
{name}
+
+ {isCurrent &&
}
+
+ );
+});
+
+export default BatchFile;