-
-
-
-
- {showLeftBar && (
-
- )}
-
-
- {/* Middle part: */}
-
- {!isFileOpened &&
}
-
-
- {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
-
- {renderSubtitles()}
-
-
- {canvasPlayerEnabled && }
-
-
- {isRotationSet && !hideCanvasPreview && (
-
-
- {t('Rotation preview')}
- {!canvasPlayerRequired && setHideCanvasPreview(true)} />}
-
- )}
-
- {isFileOpened && (
-
-
+
+
+
+
- {subtitleStreams.length > 0 &&
}
+
+
+ {showLeftBar && (
+
+ )}
+
- {!showRightBar && (
-
- )}
+ {/* Middle part: */}
+
+ {!isFileOpened &&
}
+
+
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
+
+ {renderSubtitles()}
+
+
+ {canvasPlayerEnabled && }
- )}
+
+ {isRotationSet && !hideCanvasPreview && (
+
+
+ {t('Rotation preview')}
+ {!canvasPlayerRequired && setHideCanvasPreview(true)} />}
+
+ )}
+
+ {isFileOpened && (
+
+
+
+ {subtitleStreams.length > 0 && }
+
+ {!showRightBar && (
+
+ )}
+
+ )}
+
+
+ {working && }
+
+
+ {tunerVisible &&
setTunerVisible()} />}
+
- {working && }
+ {showRightBar && isFileOpened && (
+
+
+
+ )}
-
- {tunerVisible && renderTuner(tunerVisible)}
-
- {showRightBar && isFileOpened && (
-
-
+
+
+
+
+
+ setStreamsSelectorShown(false)}
+ >
+
+
+
+
+
+
+
+
+
+
+ {t('Settings')}
+ {t('Current setting')}
+
+
+
-
- )}
-
-
+
+
+
-
-
+ 0 && concatDialogVisible} onHide={() => setConcatDialogVisible(false)} initialPaths={batchFilePaths} onConcat={userConcatFiles} setAlwaysConcatMultipleFiles={setAlwaysConcatMultipleFiles} alwaysConcatMultipleFiles={alwaysConcatMultipleFiles} />
-
-
-
- setStreamsSelectorShown(false)}
- >
-
-
-
-
-
-
-
-
-
-
- {t('Settings')}
- {t('Current setting')}
-
-
-
-
-
-
-
- 0 && concatDialogVisible} onHide={() => setConcatDialogVisible(false)} initialPaths={batchFilePaths} onConcat={userConcatFiles} segmentsToChapters={segmentsToChapters} setSegmentsToChapters={setSegmentsToChapters} setAlwaysConcatMultipleFiles={setAlwaysConcatMultipleFiles} alwaysConcatMultipleFiles={alwaysConcatMultipleFiles} preserveMetadataOnMerge={preserveMetadataOnMerge} setPreserveMetadataOnMerge={setPreserveMetadataOnMerge} preserveMovData={preserveMovData} setPreserveMovData={setPreserveMovData} />
-
- setKeyboardShortcutsVisible(false)} keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} resetKeyBindings={resetKeyBindings} />
-
-
+ setKeyboardShortcutsVisible(false)} keyBindings={keyBindings} setKeyBindings={setKeyBindings} currentCutSeg={currentCutSeg} resetKeyBindings={resetKeyBindings} />
+
+
+
);
});
diff --git a/src/BottomBar.jsx b/src/BottomBar.jsx
index ea17cb69..b101fdbf 100644
--- a/src/BottomBar.jsx
+++ b/src/BottomBar.jsx
@@ -13,11 +13,13 @@ import SegmentCutpointButton from './components/SegmentCutpointButton';
import SetCutpointButton from './components/SetCutpointButton';
import ExportButton from './components/ExportButton';
import ToggleExportConfirm from './components/ToggleExportConfirm';
+import CaptureFormatButton from './components/CaptureFormatButton';
import SimpleModeButton from './components/SimpleModeButton';
import { withBlur, toast, mirrorTransform } from './util';
import { getSegColor } from './util/colors';
import { formatDuration, parseDuration } from './util/duration';
+import useUserSettings from './hooks/useUserSettings';
const isDev = window.require('electron-is-dev');
@@ -27,9 +29,9 @@ const zoomOptions = Array(13).fill().map((unused, z) => 2 ** z);
const leftRightWidth = 100;
const BottomBar = memo(({
- zoom, setZoom, invertCutSegments, setInvertCutSegments, timelineToggleComfortZoom, simpleMode, toggleSimpleMode,
- isRotationSet, rotation, areWeCutting, increaseRotation, cleanupFilesDialog, renderCaptureFormatButton,
- captureSnapshot, onExportPress, enabledSegments, hasVideo, autoMerge, exportConfirmEnabled, toggleExportConfirmEnabled,
+ zoom, setZoom, timelineToggleComfortZoom,
+ isRotationSet, rotation, areWeCutting, increaseRotation, cleanupFilesDialog,
+ captureSnapshot, onExportPress, enabledSegments, hasVideo,
seekAbs, currentSegIndexSafe, cutSegments, currentCutSeg, setCutStart, setCutEnd,
setCurrentSegIndex, cutStartTimeManual, setCutStartTimeManual, cutEndTimeManual, setCutEndTimeManual,
jumpTimelineStart, jumpTimelineEnd, jumpCutEnd, jumpCutStart, startTimeOffset, setCutTime, currentApparentCutSeg,
@@ -38,6 +40,8 @@ const BottomBar = memo(({
}) => {
const { t } = useTranslation();
+ const { invertCutSegments, setInvertCutSegments, simpleMode, toggleSimpleMode } = useUserSettings();
+
const onYinYangClick = useCallback(() => {
setInvertCutSegments(v => {
const newVal = !v;
@@ -281,7 +285,7 @@ const BottomBar = memo(({
className="no-user-select"
style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '3px 4px' }}
>
-
+
{simpleMode &&
{t('Toggle advanced view')}
}
@@ -344,7 +348,7 @@ const BottomBar = memo(({
{hasVideo && (
<>
- {!simpleMode && renderCaptureFormatButton({ height: 20 })}
+ {!simpleMode &&
}
)}
- {!simpleMode &&
}
+ {!simpleMode &&
}
-
+
>
);
diff --git a/src/ExportConfirm.jsx b/src/ExportConfirm.jsx
index 0e811271..17e265ee 100644
--- a/src/ExportConfirm.jsx
+++ b/src/ExportConfirm.jsx
@@ -16,6 +16,7 @@ import HighlightedText from './components/HighlightedText';
import { withBlur, toast } from './util';
import { isMov as ffmpegIsMov } from './ffmpeg';
+import useUserSettings from './hooks/useUserSettings';
const sheetStyle = {
position: 'fixed',
@@ -39,16 +40,17 @@ const warningStyle = { color: '#faa', fontSize: '80%' };
const HelpIcon = ({ onClick }) =>
;
const ExportConfirm = memo(({
- autoMerge, areWeCutting, enabledSegments, willMerge, visible, onClosePress, onExportConfirm, keyframeCut, toggleKeyframeCut,
- setAutoMerge, renderOutFmt, preserveMovData, togglePreserveMovData, movFastStart, toggleMovFastStart, avoidNegativeTs, setAvoidNegativeTs,
- changeOutDir, outputDir, numStreamsTotal, numStreamsToCopy, setStreamsSelectorShown,
- exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, toggleSegmentsToChapters, outFormat,
- preserveMetadataOnMerge, togglePreserveMetadataOnMerge, outSegTemplate, setOutSegTemplate, generateOutSegFileNames,
- filePath, currentSegIndexSafe, getOutSegError, autoDeleteMergedSegments, setAutoDeleteMergedSegments,
- safeOutputFileName, toggleSafeOutputFileName, segmentsToChaptersOnly, setSegmentsToChaptersOnly, enableSmartCut, setEnableSmartCut,
+ areWeCutting, enabledSegments, willMerge, visible, onClosePress, onExportConfirm,
+ renderOutFmt,
+ outputDir, numStreamsTotal, numStreamsToCopy, setStreamsSelectorShown,
+ outFormat,
+ outSegTemplate, setOutSegTemplate, generateOutSegFileNames,
+ filePath, currentSegIndexSafe, getOutSegError,
}) => {
const { t } = useTranslation();
+ const { changeOutDir, keyframeCut, preserveMovData, movFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, togglePreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut } = useUserSettings();
+
const isMov = ffmpegIsMov(outFormat);
const isIpod = outFormat === 'ipod';
@@ -121,7 +123,7 @@ const ExportConfirm = memo(({
{t('Export options')}
- {enabledSegments.length >= 2 && {t('Merge {{segments}} cut segments to one file?', { segments: enabledSegments.length })} }
+ {enabledSegments.length >= 2 && {t('Merge {{segments}} cut segments to one file?', { segments: enabledSegments.length })} }
{t('Output container format:')} {renderOutFmt({ height: 20, maxWidth: 150 })}
@@ -135,7 +137,7 @@ const ExportConfirm = memo(({
{canEditTemplate && (
-
+
)}
@@ -166,7 +168,7 @@ const ExportConfirm = memo(({
{!enableSmartCut && (
- {t('Cut mode:')} toggleKeyframeCut(false))} />
+ {t('Cut mode:')}
{!keyframeCut && {t('Note: Keyframe cut is recommended for most common files')} }
)}
@@ -176,11 +178,11 @@ const ExportConfirm = memo(({
{isMov && (
<>
- {t('Enable MOV Faststart?')}
+ {t('Enable MOV Faststart?')}
{isIpod && !movFastStart && {t('For the ipod format, it is recommended to activate this option')} }
- {t('Preserve all MP4/MOV metadata?')}
+ {t('Preserve all MP4/MOV metadata?')}
{isIpod && preserveMovData && {t('For the ipod format, it is recommended to deactivate this option')} }
>
@@ -211,7 +213,7 @@ const ExportConfirm = memo(({
transition={{ duration: 0.4, easings: ['easeOut'] }}
style={{ display: 'flex', alignItems: 'flex-end' }}
>
-
+
{t('Show this page before exporting?')}
@@ -222,7 +224,7 @@ const ExportConfirm = memo(({
exit={{ scale: 0.7, opacity: 0 }}
transition={{ duration: 0.4, easings: ['easeOut'] }}
>
-
onExportConfirm()} size={1.7} />
+ onExportConfirm()} size={1.7} />
>
diff --git a/src/NoFileLoaded.jsx b/src/NoFileLoaded.jsx
index d756f457..82abba80 100644
--- a/src/NoFileLoaded.jsx
+++ b/src/NoFileLoaded.jsx
@@ -5,11 +5,13 @@ import { useTranslation, Trans } from 'react-i18next';
import SetCutpointButton from './components/SetCutpointButton';
import SimpleModeButton from './components/SimpleModeButton';
+import useUserSettings from './hooks/useUserSettings';
const electron = window.require('electron');
-const NoFileLoaded = memo(({ mifiLink, toggleHelp, currentCutSeg, simpleMode, toggleSimpleMode }) => {
+const NoFileLoaded = memo(({ mifiLink, toggleHelp, currentCutSeg }) => {
const { t } = useTranslation();
+ const { simpleMode, toggleSimpleMode } = useUserSettings();
return (
@@ -24,7 +26,7 @@ const NoFileLoaded = memo(({ mifiLink, toggleHelp, currentCutSeg, simpleMode, to
- {simpleMode ? i18n.t('to show advanced view') : i18n.t('to show simple view')}
+ {simpleMode ? i18n.t('to show advanced view') : i18n.t('to show simple view')}
diff --git a/src/SegmentList.jsx b/src/SegmentList.jsx
index 0dff18a4..801f74d1 100644
--- a/src/SegmentList.jsx
+++ b/src/SegmentList.jsx
@@ -10,6 +10,7 @@ import useDebounce from 'react-use/lib/useDebounce';
import scrollIntoView from 'scroll-into-view-if-needed';
import useContextMenu from './hooks/useContextMenu';
+import useUserSettings from './hooks/useUserSettings';
import { saveColor } from './colors';
import { getSegColor } from './util/colors';
@@ -124,14 +125,16 @@ const Segment = memo(({ seg, index, currentSegIndex, formatTimecode, getFrameCou
const SegmentList = memo(({
formatTimecode, apparentCutSegments, inverseCutSegments, getFrameCount, onSegClick,
- currentSegIndex, invertCutSegments,
+ currentSegIndex,
updateSegOrder, updateSegOrders, addCutSegment, removeCutSegment,
onLabelSegmentPress, currentCutSeg, segmentAtCursor, toggleSegmentsList, splitCurrentSegment,
enabledSegments, enabledSegmentsRaw, onExportSingleSegmentClick, onExportSegmentEnabledToggle, onExportSegmentDisableAll, onExportSegmentEnableAll,
- jumpSegStart, jumpSegEnd, simpleMode, onViewSegmentTagsPress,
+ jumpSegStart, jumpSegEnd, onViewSegmentTagsPress,
}) => {
const { t } = useTranslation();
+ const { invertCutSegments, simpleMode } = useUserSettings();
+
const segments = invertCutSegments ? inverseCutSegments : apparentCutSegments;
const sortableList = segments.map((seg) => ({ id: seg.segId, seg }));
diff --git a/src/Settings.jsx b/src/Settings.jsx
index 361ea2c0..38513d25 100644
--- a/src/Settings.jsx
+++ b/src/Settings.jsx
@@ -2,6 +2,9 @@ import React, { memo, useCallback, useMemo } from 'react';
import { FaYinYang, FaKeyboard } from 'react-icons/fa';
import { Button, Table, NumericalIcon, KeyIcon, FolderCloseIcon, DocumentIcon, TimeIcon, Checkbox, Select } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
+import CaptureFormatButton from './components/CaptureFormatButton';
+import AutoExportToggler from './components/AutoExportToggler';
+import useUserSettings from './hooks/useUserSettings';
// https://www.electronjs.org/docs/api/locales
@@ -36,17 +39,13 @@ const Row = (props) =>
;
const KeyCell = (props) =>
;
const Settings = memo(({
- changeOutDir, customOutDir, keyframeCut, setKeyframeCut, invertCutSegments, setInvertCutSegments,
- autoSaveProjectFile, setAutoSaveProjectFile, timecodeFormat, setTimecodeFormat, askBeforeClose, setAskBeforeClose,
- AutoExportToggler, renderCaptureFormatButton, onTunerRequested, language, setLanguage,
- invertTimelineScroll, setInvertTimelineScroll, ffmpegExperimental, setFfmpegExperimental,
- enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction,
- hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode,
- enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify,
+ onTunerRequested,
onKeyboardShortcutsDialogRequested,
}) => {
const { t } = useTranslation();
+ const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify } = useUserSettings();
+
const onLangChange = useCallback((e) => {
const { value } = e.target;
const l = value !== '' ? value : undefined;
@@ -115,8 +114,8 @@ const Settings = memo(({
{t('Normal cut')} : {t('Accurate time but could leave an empty portion at the beginning of the video. Equiv to')}
ffmpeg -i -ss ...
- setKeyframeCut(keyframeCut === 'keyframe' ? 'normal' : 'keyframe')}>
- {keyframeCut === 'keyframe' ? t('Keyframe cut') : t('Normal cut')}
+ toggleKeyframeCut()}>
+ {keyframeCut ? t('Keyframe cut') : t('Normal cut')}
@@ -174,7 +173,7 @@ const Settings = memo(({
{t('Snapshot capture format')}
- {renderCaptureFormatButton()}
+
diff --git a/src/StreamsSelector.jsx b/src/StreamsSelector.jsx
index c04f5e73..7d8a9654 100644
--- a/src/StreamsSelector.jsx
+++ b/src/StreamsSelector.jsx
@@ -7,6 +7,7 @@ import { MdSubtitles } from 'react-icons/md';
import { BookIcon, Paragraph, TextInput, MoreIcon, Position, Popover, Menu, TrashIcon, EditIcon, InfoSignIcon, IconButton, Select, Heading, SortAscIcon, SortDescIcon, Dialog, Button, PlusIcon, Pane, ForkIcon, Alert } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
+import AutoExportToggler from './components/AutoExportToggler';
import { askForMetadataKey, showJson5Dialog } from './dialogs';
import { formatDuration } from './util/duration';
import { getStreamFps } from './ffmpeg';
@@ -325,7 +326,7 @@ const StreamsSelector = memo(({
mainFilePath, mainFileFormatData, streams: mainFileStreams, mainFileChapters, isCopyingStreamId, toggleCopyStreamId,
setCopyStreamIdsForPath, onExtractStreamPress, onExtractAllStreamsPress, allFilesMeta, externalFilesMeta, setExternalFilesMeta,
showAddStreamSourceDialog, shortestFlag, setShortestFlag, nonCopiedExtraStreams,
- AutoExportToggler, customTagsByFile, setCustomTagsByFile, customTagsByStreamId, setCustomTagsByStreamId,
+ customTagsByFile, setCustomTagsByFile, customTagsByStreamId, setCustomTagsByStreamId,
dispositionByStreamId, setDispositionByStreamId,
}) => {
const [editingFile, setEditingFile] = useState();
diff --git a/src/Timeline.jsx b/src/Timeline.jsx
index a01dc638..f73cb30f 100644
--- a/src/Timeline.jsx
+++ b/src/Timeline.jsx
@@ -8,6 +8,7 @@ import { FaCaretDown, FaCaretUp } from 'react-icons/fa';
import TimelineSeg from './TimelineSeg';
import BetweenSegments from './BetweenSegments';
import useContextMenu from './hooks/useContextMenu';
+import useUserSettings from './hooks/useUserSettings';
import { timelineBackground } from './colors';
@@ -59,13 +60,15 @@ const CommandedTime = memo(({ commandedTimePercent }) => {
const Timeline = memo(({
durationSafe, getCurrentTime, startTimeOffset, playerTime, commandedTime,
zoom, neighbouringKeyFrames, seekAbs, apparentCutSegments,
- setCurrentSegIndex, currentSegIndexSafe, invertCutSegments, inverseCutSegments, formatTimecode,
+ setCurrentSegIndex, currentSegIndexSafe, inverseCutSegments, formatTimecode,
waveforms, shouldShowWaveform, shouldShowKeyframes, timelineHeight = 36, thumbnails,
onZoomWindowStartTimeChange, waveformEnabled, thumbnailsEnabled,
playing, isFileOpened, onWheel, commandedTimeRef, goToTimecode,
}) => {
const { t } = useTranslation();
+ const { invertCutSegments } = useUserSettings();
+
const timelineScrollerRef = useRef();
const timelineScrollerSkipEventRef = useRef();
const timelineScrollerSkipEventDebounce = useRef();
diff --git a/src/TopMenu.jsx b/src/TopMenu.jsx
index a2042c45..619a21fa 100644
--- a/src/TopMenu.jsx
+++ b/src/TopMenu.jsx
@@ -1,4 +1,4 @@
-import React, { memo } from 'react';
+import React, { memo, useCallback } from 'react';
import { IoIosHelpCircle, IoIosSettings } from 'react-icons/io';
import { FaLock, FaUnlock } from 'react-icons/fa';
import { IconButton, Button, CrossIcon, ListIcon, VolumeUpIcon, VolumeOffIcon } from 'evergreen-ui';
@@ -8,15 +8,18 @@ import MergeExportButton from './components/MergeExportButton';
import { withBlur, isMasBuild } from './util';
import { primaryTextColor, controlsBackground } from './colors';
+import useUserSettings from './hooks/useUserSettings';
const TopMenu = memo(({
- filePath, copyAnyAudioTrack, toggleStripAudio, customOutDir, changeOutDir,
+ filePath, fileFormat, copyAnyAudioTrack, toggleStripAudio,
renderOutFmt, toggleHelp, numStreamsToCopy, numStreamsTotal, setStreamsSelectorShown, toggleSettings,
- enabledSegments, autoMerge, setAutoMerge, autoDeleteMergedSegments, setAutoDeleteMergedSegments, isCustomFormatSelected, onOutFormatLockedClick, simpleMode, outFormatLocked, clearOutDir,
- segmentsToChaptersOnly, setSegmentsToChaptersOnly,
+ enabledSegments, isCustomFormatSelected, clearOutDir,
}) => {
const { t } = useTranslation();
+ const { customOutDir, changeOutDir, simpleMode, outFormatLocked, setOutFormatLocked } = useUserSettings();
+
+ const onOutFormatLockedClick = useCallback(() => setOutFormatLocked((v) => (v ? undefined : fileFormat)), [fileFormat, setOutFormatLocked]);
// We cannot allow exporting to a directory which has not yet been confirmed by an open dialog because of sandox restrictions
const showClearWorkingDirButton = customOutDir && !isMasBuild;
@@ -75,7 +78,7 @@ const TopMenu = memo(({
{!simpleMode && (isCustomFormatSelected || outFormatLocked) && renderFormatLock()}
-
+
>
)}
diff --git a/src/components/AutoExportToggler.jsx b/src/components/AutoExportToggler.jsx
new file mode 100644
index 00000000..3bc3a6b6
--- /dev/null
+++ b/src/components/AutoExportToggler.jsx
@@ -0,0 +1,18 @@
+import React, { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { Button, ForkIcon, DisableIcon } from 'evergreen-ui';
+
+import useUserSettings from '../hooks/useUserSettings';
+
+const AutoExportToggler = memo(() => {
+ const { t } = useTranslation();
+ const { autoExportExtraStreams, setAutoExportExtraStreams } = useUserSettings();
+
+ return (
+
setAutoExportExtraStreams(!autoExportExtraStreams)}>
+ {autoExportExtraStreams ? t('Extract') : t('Discard')}
+
+ );
+});
+
+export default AutoExportToggler;
diff --git a/src/components/CaptureFormatButton.jsx b/src/components/CaptureFormatButton.jsx
new file mode 100644
index 00000000..5d563e7f
--- /dev/null
+++ b/src/components/CaptureFormatButton.jsx
@@ -0,0 +1,25 @@
+import React, { memo } from 'react';
+import { Button } from 'evergreen-ui';
+import { useTranslation } from 'react-i18next';
+import { FaImage } from 'react-icons/fa';
+
+import useUserSettings from '../hooks/useUserSettings';
+import { withBlur } from '../util';
+
+const CaptureFormatButton = memo(({ showIcon = false, ...props }) => {
+ const { t } = useTranslation();
+ const { captureFormat, toggleCaptureFormat } = useUserSettings();
+ return (
+
: undefined}
+ title={t('Capture frame format')}
+ onClick={withBlur(toggleCaptureFormat)}
+ // eslint-disable-next-line react/jsx-props-no-spreading
+ {...props}
+ >
+ {captureFormat}
+
+ );
+});
+
+export default CaptureFormatButton;
diff --git a/src/components/ConcatDialog.jsx b/src/components/ConcatDialog.jsx
index e828869c..9693cdd1 100644
--- a/src/components/ConcatDialog.jsx
+++ b/src/components/ConcatDialog.jsx
@@ -6,6 +6,7 @@ import { AiOutlineMergeCells } from 'react-icons/ai';
import { readFileMeta, getSmarterOutFormat } from '../ffmpeg';
import useFileFormatState from '../hooks/useFileFormatState';
import OutputFormatSelect from './OutputFormatSelect';
+import useUserSettings from '../hooks/useUserSettings';
const { basename } = window.require('path');
@@ -17,12 +18,10 @@ const rowStyle = {
const ConcatDialog = memo(({
isShown, onHide, initialPaths, onConcat,
- segmentsToChapters, setSegmentsToChapters,
alwaysConcatMultipleFiles, setAlwaysConcatMultipleFiles,
- preserveMetadataOnMerge, setPreserveMetadataOnMerge,
- preserveMovData, setPreserveMovData,
}) => {
const { t } = useTranslation();
+ const { preserveMovData, setPreserveMovData, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge } = useUserSettings();
const [paths, setPaths] = useState(initialPaths);
const [includeAllStreams, setIncludeAllStreams] = useState(false);
diff --git a/src/components/CustomOutDirButton.jsx b/src/components/CustomOutDirButton.jsx
deleted file mode 100644
index 7db07b59..00000000
--- a/src/components/CustomOutDirButton.jsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React, { memo } from 'react';
-import { Button, FolderOpenIcon } from 'evergreen-ui';
-import { useTranslation } from 'react-i18next';
-
-import { withBlur } from '../util';
-
-
-const CustomOutDirButton = memo(({ customOutDir, changeOutDir }) => {
- const { t } = useTranslation();
-
- return (
-
- {customOutDir ? t('Working dir set') : t('Working dir unset')}
-
- );
-});
-
-export default CustomOutDirButton;
diff --git a/src/components/ExportButton.jsx b/src/components/ExportButton.jsx
index c2bc2f46..0974b355 100644
--- a/src/components/ExportButton.jsx
+++ b/src/components/ExportButton.jsx
@@ -4,13 +4,16 @@ import { FaFileExport } from 'react-icons/fa';
import { useTranslation } from 'react-i18next';
import { primaryColor } from '../colors';
+import useUserSettings from '../hooks/useUserSettings';
-const ExportButton = memo(({ enabledSegments, areWeCutting, autoMerge, onClick, size = 1 }) => {
+const ExportButton = memo(({ enabledSegments, areWeCutting, onClick, size = 1 }) => {
const CutIcon = areWeCutting ? FiScissors : FaFileExport;
const { t } = useTranslation();
+ const { autoMerge } = useUserSettings();
+
let exportButtonTitle = t('Export');
if (enabledSegments.length === 1) {
exportButtonTitle = t('Export selection');
diff --git a/src/components/KeyframeCutButton.jsx b/src/components/KeyframeCutButton.jsx
index b8d45231..60dfb233 100644
--- a/src/components/KeyframeCutButton.jsx
+++ b/src/components/KeyframeCutButton.jsx
@@ -3,17 +3,19 @@ import { Button, KeyIcon } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
import { withBlur } from '../util';
+import useUserSettings from '../hooks/useUserSettings';
-const KeyframeCutButton = memo(({ keyframeCut, onClick }) => {
+const KeyframeCutButton = memo(() => {
const { t } = useTranslation();
+ const { keyframeCut, toggleKeyframeCut } = useUserSettings();
return (
toggleKeyframeCut(false))}
>
{keyframeCut ? t('Keyframe cut') : t('Normal cut')}
diff --git a/src/components/MergeExportButton.jsx b/src/components/MergeExportButton.jsx
index d8a1bbab..9681f4ee 100644
--- a/src/components/MergeExportButton.jsx
+++ b/src/components/MergeExportButton.jsx
@@ -4,11 +4,14 @@ import { useTranslation } from 'react-i18next';
import { MdCallSplit, MdCallMerge } from 'react-icons/md';
import { withBlur } from '../util';
+import useUserSettings from '../hooks/useUserSettings';
-const MergeExportButton = memo(({ autoMerge, enabledSegments, setAutoMerge, autoDeleteMergedSegments, setAutoDeleteMergedSegments, segmentsToChaptersOnly, setSegmentsToChaptersOnly }) => {
+const MergeExportButton = memo(({ enabledSegments }) => {
const { t } = useTranslation();
+ const { autoMerge, setAutoMerge, autoDeleteMergedSegments, setAutoDeleteMergedSegments, segmentsToChaptersOnly, setSegmentsToChaptersOnly } = useUserSettings();
+
let AutoMergeIcon;
let effectiveMode;
diff --git a/src/components/MovFastStartButton.jsx b/src/components/MovFastStartButton.jsx
index df20896c..40699e90 100644
--- a/src/components/MovFastStartButton.jsx
+++ b/src/components/MovFastStartButton.jsx
@@ -3,10 +3,12 @@ import { Button } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
import { withBlur } from '../util';
+import useUserSettings from '../hooks/useUserSettings';
-const MovFastStartButton = memo(({ movFastStart, toggleMovFastStart }) => {
+const MovFastStartButton = memo(() => {
const { t } = useTranslation();
+ const { movFastStart, toggleMovFastStart } = useUserSettings();
return (
diff --git a/src/components/OutSegTemplateEditor.jsx b/src/components/OutSegTemplateEditor.jsx
index 7f136a0a..3e161378 100644
--- a/src/components/OutSegTemplateEditor.jsx
+++ b/src/components/OutSegTemplateEditor.jsx
@@ -8,13 +8,16 @@ import withReactContent from 'sweetalert2-react-content';
import HighlightedText from './HighlightedText';
import { defaultOutSegTemplate } from '../util';
+import useUserSettings from '../hooks/useUserSettings';
const ReactSwal = withReactContent(Swal);
const inputStyle = { flexGrow: 1, fontFamily: 'inherit', fontSize: '.8em' };
-const OutSegTemplateEditor = memo(({ helpIcon, outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe, getOutSegError, safeOutputFileName, toggleSafeOutputFileName }) => {
+const OutSegTemplateEditor = memo(({ helpIcon, outSegTemplate, setOutSegTemplate, generateOutSegFileNames, currentSegIndexSafe, getOutSegError }) => {
+ const { safeOutputFileName, toggleSafeOutputFileName } = useUserSettings();
+
const [text, setText] = useState(outSegTemplate);
const [debouncedText] = useDebounce(text, 500);
const [validText, setValidText] = useState();
diff --git a/src/components/PreserveMovDataButton.jsx b/src/components/PreserveMovDataButton.jsx
index e9f1384f..74ee1157 100644
--- a/src/components/PreserveMovDataButton.jsx
+++ b/src/components/PreserveMovDataButton.jsx
@@ -3,10 +3,12 @@ import { Button } from 'evergreen-ui';
import { useTranslation } from 'react-i18next';
import { withBlur } from '../util';
+import useUserSettings from '../hooks/useUserSettings';
-const PreserveMovDataButton = memo(({ preserveMovData, togglePreserveMovData }) => {
+const PreserveMovDataButton = memo(() => {
const { t } = useTranslation();
+ const { preserveMovData, togglePreserveMovData } = useUserSettings();
return (
diff --git a/src/components/SimpleModeButton.jsx b/src/components/SimpleModeButton.jsx
index aa5ab048..c643e6ba 100644
--- a/src/components/SimpleModeButton.jsx
+++ b/src/components/SimpleModeButton.jsx
@@ -3,10 +3,12 @@ import { useTranslation } from 'react-i18next';
import { FaBaby } from 'react-icons/fa';
import { primaryTextColor } from '../colors';
+import useUserSettings from '../hooks/useUserSettings';
-const SimpleModeButton = memo(({ simpleMode, toggleSimpleMode, size = 20, style }) => {
+const SimpleModeButton = memo(({ size = 20, style }) => {
const { t } = useTranslation();
+ const { simpleMode, toggleSimpleMode } = useUserSettings();
return (
{
+const ToggleExportConfirm = memo(({ size = 23, style }) => {
const { t } = useTranslation();
+ const { exportConfirmEnabled, toggleExportConfirmEnabled } = useUserSettings();
return (
diff --git a/src/components/ValueTuners.jsx b/src/components/ValueTuners.jsx
new file mode 100644
index 00000000..6d57d3d6
--- /dev/null
+++ b/src/components/ValueTuners.jsx
@@ -0,0 +1,44 @@
+import React, { memo } from 'react';
+import { useTranslation } from 'react-i18next';
+
+import ValueTuner from './ValueTuner';
+import useUserSettings from '../hooks/useUserSettings';
+
+const ValueTuners = memo(({ type, onFinished }) => {
+ const { t } = useTranslation();
+ const { wheelSensitivity, setWheelSensitivity, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, keyboardSeekAccFactor, setKeyboardSeekAccFactor } = useUserSettings();
+
+ // NOTE default values are duplicated in public/configStore.js
+ const types = {
+ wheelSensitivity: {
+ title: t('Timeline trackpad/wheel sensitivity'),
+ value: wheelSensitivity,
+ setValue: setWheelSensitivity,
+ default: 0.2,
+ },
+ keyboardNormalSeekSpeed: {
+ title: t('Timeline keyboard seek speed'),
+ value: keyboardNormalSeekSpeed,
+ setValue: setKeyboardNormalSeekSpeed,
+ min: 0,
+ max: 100,
+ default: 1,
+ },
+ keyboardSeekAccFactor: {
+ title: t('Timeline keyboard seek acceleration'),
+ value: keyboardSeekAccFactor,
+ setValue: setKeyboardSeekAccFactor,
+ min: 1,
+ max: 2,
+ default: 1.03,
+ },
+ };
+ const { title, value, setValue, min, max, default: defaultValue } = types[type];
+
+ const resetToDefault = () => setValue(defaultValue);
+
+ return ;
+});
+
+
+export default ValueTuners;
diff --git a/src/contexts/UserSettingsContext.js b/src/contexts/UserSettingsContext.js
new file mode 100644
index 00000000..6353dc06
--- /dev/null
+++ b/src/contexts/UserSettingsContext.js
@@ -0,0 +1,3 @@
+import React from 'react';
+
+export default React.createContext();
diff --git a/src/hooks/useUserSettings.js b/src/hooks/useUserSettings.js
new file mode 100644
index 00000000..78f0cffc
--- /dev/null
+++ b/src/hooks/useUserSettings.js
@@ -0,0 +1,5 @@
+import { useContext } from 'react';
+
+import UserSettingsContext from '../contexts/UserSettingsContext';
+
+export default () => useContext(UserSettingsContext);
diff --git a/src/hooks/useUserPreferences.js b/src/hooks/useUserSettingsRoot.js
similarity index 100%
rename from src/hooks/useUserPreferences.js
rename to src/hooks/useUserSettingsRoot.js