lossless-cut/src/hooks/useUserSettingsRoot.ts

274 wiersze
14 KiB
TypeScript

import { useEffect, useState, useRef, useCallback } from 'react';
import i18n from 'i18next';
import { StoreGetConfig, StoreResetConfig, StoreSetConfig, Config } from '../../types';
import { errorToast } from '../swal';
import isDev from '../isDev';
const remote = window.require('@electron/remote');
const configStore: { get: StoreGetConfig, set: StoreSetConfig, reset: StoreResetConfig } = remote.require('./configStore');
export default () => {
const firstUpdateRef = useRef(true);
function safeSetConfig<T extends keyof Config>(keyValue: Record<T, Config[T]>) {
const entry = Object.entries(keyValue)[0]!;
const key = entry[0] as T;
const value = entry[1] as Config[T];
// Prevent flood-saving all config during mount
if (firstUpdateRef.current) return;
if (isDev) console.log('save', key, value);
try {
configStore.set(key, value);
} catch (err) {
console.error('Failed to set config', key, err);
errorToast(i18n.t('Unable to save your preferences. Try to disable any anti-virus'));
}
}
function safeGetConfig<T extends keyof Config>(key: T) {
const rawVal = configStore.get(key);
if (rawVal === undefined) return undefined as typeof rawVal;
// NOTE: Need to clone any non-primitive in renderer, or it will become very slow
// I think because Electron is proxying objects over the bridge
const cloned: typeof rawVal = JSON.parse(JSON.stringify(rawVal));
return cloned;
}
// From https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
// If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render
// Without this there was a huge performance issue https://github.com/mifi/lossless-cut/issues/1097
const safeGetConfigInitial = <T extends keyof Config>(key: T) => () => safeGetConfig(key);
const [captureFormat, setCaptureFormat] = useState(safeGetConfigInitial('captureFormat'));
useEffect(() => safeSetConfig({ captureFormat }), [captureFormat]);
const [customOutDir, setCustomOutDir] = useState(safeGetConfigInitial('customOutDir'));
useEffect(() => safeSetConfig({ customOutDir }), [customOutDir]);
const [keyframeCut, setKeyframeCut] = useState(safeGetConfigInitial('keyframeCut'));
useEffect(() => safeSetConfig({ keyframeCut }), [keyframeCut]);
const [preserveMovData, setPreserveMovData] = useState(safeGetConfigInitial('preserveMovData'));
useEffect(() => safeSetConfig({ preserveMovData }), [preserveMovData]);
const [movFastStart, setMovFastStart] = useState(safeGetConfigInitial('movFastStart'));
useEffect(() => safeSetConfig({ movFastStart }), [movFastStart]);
const [avoidNegativeTs, setAvoidNegativeTs] = useState(safeGetConfigInitial('avoidNegativeTs'));
useEffect(() => safeSetConfig({ avoidNegativeTs }), [avoidNegativeTs]);
const [autoMerge, setAutoMerge] = useState(safeGetConfigInitial('autoMerge'));
useEffect(() => safeSetConfig({ autoMerge }), [autoMerge]);
const [timecodeFormat, setTimecodeFormat] = useState(safeGetConfigInitial('timecodeFormat'));
useEffect(() => safeSetConfig({ timecodeFormat }), [timecodeFormat]);
const [invertCutSegments, setInvertCutSegments] = useState(safeGetConfigInitial('invertCutSegments'));
useEffect(() => safeSetConfig({ invertCutSegments }), [invertCutSegments]);
const [autoExportExtraStreams, setAutoExportExtraStreams] = useState(safeGetConfigInitial('autoExportExtraStreams'));
useEffect(() => safeSetConfig({ autoExportExtraStreams }), [autoExportExtraStreams]);
const [askBeforeClose, setAskBeforeClose] = useState(safeGetConfigInitial('askBeforeClose'));
useEffect(() => safeSetConfig({ askBeforeClose }), [askBeforeClose]);
const [enableAskForImportChapters, setEnableAskForImportChapters] = useState(safeGetConfigInitial('enableAskForImportChapters'));
useEffect(() => safeSetConfig({ enableAskForImportChapters }), [enableAskForImportChapters]);
const [enableAskForFileOpenAction, setEnableAskForFileOpenAction] = useState(safeGetConfigInitial('enableAskForFileOpenAction'));
useEffect(() => safeSetConfig({ enableAskForFileOpenAction }), [enableAskForFileOpenAction]);
const [playbackVolume, setPlaybackVolume] = useState(safeGetConfigInitial('playbackVolume'));
useEffect(() => safeSetConfig({ playbackVolume }), [playbackVolume]);
const [autoSaveProjectFile, setAutoSaveProjectFile] = useState(safeGetConfigInitial('autoSaveProjectFile'));
useEffect(() => safeSetConfig({ autoSaveProjectFile }), [autoSaveProjectFile]);
const [wheelSensitivity, setWheelSensitivity] = useState(safeGetConfigInitial('wheelSensitivity'));
useEffect(() => safeSetConfig({ wheelSensitivity }), [wheelSensitivity]);
const [invertTimelineScroll, setInvertTimelineScroll] = useState(safeGetConfigInitial('invertTimelineScroll'));
useEffect(() => safeSetConfig({ invertTimelineScroll }), [invertTimelineScroll]);
const [language, setLanguage] = useState(safeGetConfigInitial('language'));
useEffect(() => safeSetConfig({ language }), [language]);
const [ffmpegExperimental, setFfmpegExperimental] = useState(safeGetConfigInitial('ffmpegExperimental'));
useEffect(() => safeSetConfig({ ffmpegExperimental }), [ffmpegExperimental]);
const [hideNotifications, setHideNotifications] = useState(safeGetConfigInitial('hideNotifications'));
useEffect(() => safeSetConfig({ hideNotifications }), [hideNotifications]);
const [autoLoadTimecode, setAutoLoadTimecode] = useState(safeGetConfigInitial('autoLoadTimecode'));
useEffect(() => safeSetConfig({ autoLoadTimecode }), [autoLoadTimecode]);
const [autoDeleteMergedSegments, setAutoDeleteMergedSegments] = useState(safeGetConfigInitial('autoDeleteMergedSegments'));
useEffect(() => safeSetConfig({ autoDeleteMergedSegments }), [autoDeleteMergedSegments]);
const [exportConfirmEnabled, setExportConfirmEnabled] = useState(safeGetConfigInitial('exportConfirmEnabled'));
useEffect(() => safeSetConfig({ exportConfirmEnabled }), [exportConfirmEnabled]);
const [segmentsToChapters, setSegmentsToChapters] = useState(safeGetConfigInitial('segmentsToChapters'));
useEffect(() => safeSetConfig({ segmentsToChapters }), [segmentsToChapters]);
const [preserveMetadataOnMerge, setPreserveMetadataOnMerge] = useState(safeGetConfigInitial('preserveMetadataOnMerge'));
useEffect(() => safeSetConfig({ preserveMetadataOnMerge }), [preserveMetadataOnMerge]);
const [simpleMode, setSimpleMode] = useState(safeGetConfigInitial('simpleMode'));
useEffect(() => safeSetConfig({ simpleMode }), [simpleMode]);
const [outSegTemplate, setOutSegTemplate] = useState(safeGetConfigInitial('outSegTemplate'));
useEffect(() => safeSetConfig({ outSegTemplate }), [outSegTemplate]);
const [keyboardSeekAccFactor, setKeyboardSeekAccFactor] = useState(safeGetConfigInitial('keyboardSeekAccFactor'));
useEffect(() => safeSetConfig({ keyboardSeekAccFactor }), [keyboardSeekAccFactor]);
const [keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed] = useState(safeGetConfigInitial('keyboardNormalSeekSpeed'));
useEffect(() => safeSetConfig({ keyboardNormalSeekSpeed }), [keyboardNormalSeekSpeed]);
const [treatInputFileModifiedTimeAsStart, setTreatInputFileModifiedTimeAsStart] = useState(safeGetConfigInitial('treatInputFileModifiedTimeAsStart'));
useEffect(() => safeSetConfig({ treatInputFileModifiedTimeAsStart }), [treatInputFileModifiedTimeAsStart]);
const [treatOutputFileModifiedTimeAsStart, setTreatOutputFileModifiedTimeAsStart] = useState(safeGetConfigInitial('treatOutputFileModifiedTimeAsStart'));
useEffect(() => safeSetConfig({ treatOutputFileModifiedTimeAsStart }), [treatOutputFileModifiedTimeAsStart]);
const [outFormatLocked, setOutFormatLocked] = useState(safeGetConfigInitial('outFormatLocked'));
useEffect(() => safeSetConfig({ outFormatLocked }), [outFormatLocked]);
const [safeOutputFileName, setSafeOutputFileName] = useState(safeGetConfigInitial('safeOutputFileName'));
useEffect(() => safeSetConfig({ safeOutputFileName }), [safeOutputFileName]);
const [enableAutoHtml5ify, setEnableAutoHtml5ify] = useState(safeGetConfigInitial('enableAutoHtml5ify'));
useEffect(() => safeSetConfig({ enableAutoHtml5ify }), [enableAutoHtml5ify]);
const [segmentsToChaptersOnly, setSegmentsToChaptersOnly] = useState(safeGetConfigInitial('segmentsToChaptersOnly'));
useEffect(() => safeSetConfig({ segmentsToChaptersOnly }), [segmentsToChaptersOnly]);
const [keyBindings, setKeyBindings] = useState(safeGetConfigInitial('keyBindings'));
useEffect(() => safeSetConfig({ keyBindings }), [keyBindings]);
const [enableSmartCut, setEnableSmartCut] = useState(safeGetConfigInitial('enableSmartCut'));
useEffect(() => safeSetConfig({ enableSmartCut }), [enableSmartCut]);
const [customFfPath, setCustomFfPath] = useState(safeGetConfigInitial('customFfPath'));
useEffect(() => safeSetConfig({ customFfPath }), [customFfPath]);
const [storeProjectInWorkingDir, setStoreProjectInWorkingDir] = useState(safeGetConfigInitial('storeProjectInWorkingDir'));
useEffect(() => safeSetConfig({ storeProjectInWorkingDir }), [storeProjectInWorkingDir]);
const [enableOverwriteOutput, setEnableOverwriteOutput] = useState(safeGetConfigInitial('enableOverwriteOutput'));
useEffect(() => safeSetConfig({ enableOverwriteOutput }), [enableOverwriteOutput]);
const [mouseWheelZoomModifierKey, setMouseWheelZoomModifierKey] = useState(safeGetConfigInitial('mouseWheelZoomModifierKey'));
useEffect(() => safeSetConfig({ mouseWheelZoomModifierKey }), [mouseWheelZoomModifierKey]);
const [captureFrameMethod, setCaptureFrameMethod] = useState(safeGetConfigInitial('captureFrameMethod'));
useEffect(() => safeSetConfig({ captureFrameMethod }), [captureFrameMethod]);
const [captureFrameQuality, setCaptureFrameQuality] = useState(safeGetConfigInitial('captureFrameQuality'));
useEffect(() => safeSetConfig({ captureFrameQuality }), [captureFrameQuality]);
const [captureFrameFileNameFormat, setCaptureFrameFileNameFormat] = useState(safeGetConfigInitial('captureFrameFileNameFormat'));
useEffect(() => safeSetConfig({ captureFrameFileNameFormat }), [captureFrameFileNameFormat]);
const [enableNativeHevc, setEnableNativeHevc] = useState(safeGetConfigInitial('enableNativeHevc'));
useEffect(() => safeSetConfig({ enableNativeHevc }), [enableNativeHevc]);
const [enableUpdateCheck, setEnableUpdateCheck] = useState(safeGetConfigInitial('enableUpdateCheck'));
useEffect(() => safeSetConfig({ enableUpdateCheck }), [enableUpdateCheck]);
const [cleanupChoices, setCleanupChoices] = useState(safeGetConfigInitial('cleanupChoices'));
useEffect(() => safeSetConfig({ cleanupChoices }), [cleanupChoices]);
const [allowMultipleInstances, setAllowMultipleInstances] = useState(safeGetConfigInitial('allowMultipleInstances'));
useEffect(() => safeSetConfig({ allowMultipleInstances }), [allowMultipleInstances]);
const [darkMode, setDarkMode] = useState(safeGetConfigInitial('darkMode'));
useEffect(() => safeSetConfig({ darkMode }), [darkMode]);
const [preferStrongColors, setPreferStrongColors] = useState(safeGetConfigInitial('preferStrongColors'));
useEffect(() => safeSetConfig({ preferStrongColors }), [preferStrongColors]);
const [outputFileNameMinZeroPadding, setOutputFileNameMinZeroPadding] = useState(safeGetConfigInitial('outputFileNameMinZeroPadding'));
useEffect(() => safeSetConfig({ outputFileNameMinZeroPadding }), [outputFileNameMinZeroPadding]);
const [cutFromAdjustmentFrames, setCutFromAdjustmentFrames] = useState(safeGetConfigInitial('cutFromAdjustmentFrames'));
useEffect(() => safeSetConfig({ cutFromAdjustmentFrames }), [cutFromAdjustmentFrames]);
const resetKeyBindings = useCallback(() => {
configStore.reset('keyBindings');
setKeyBindings(safeGetConfig('keyBindings'));
}, []);
// NOTE! This useEffect must be placed after all usages of firstUpdateRef.current (safeSetConfig)
useEffect(() => {
firstUpdateRef.current = false;
return () => {
firstUpdateRef.current = true;
};
}, []);
return {
captureFormat,
setCaptureFormat,
customOutDir,
setCustomOutDir,
keyframeCut,
setKeyframeCut,
preserveMovData,
setPreserveMovData,
movFastStart,
setMovFastStart,
avoidNegativeTs,
setAvoidNegativeTs,
autoMerge,
setAutoMerge,
timecodeFormat,
setTimecodeFormat,
invertCutSegments,
setInvertCutSegments,
autoExportExtraStreams,
setAutoExportExtraStreams,
askBeforeClose,
setAskBeforeClose,
enableAskForImportChapters,
setEnableAskForImportChapters,
enableAskForFileOpenAction,
setEnableAskForFileOpenAction,
playbackVolume,
setPlaybackVolume,
autoSaveProjectFile,
setAutoSaveProjectFile,
wheelSensitivity,
setWheelSensitivity,
invertTimelineScroll,
setInvertTimelineScroll,
language,
setLanguage,
ffmpegExperimental,
setFfmpegExperimental,
hideNotifications,
setHideNotifications,
autoLoadTimecode,
setAutoLoadTimecode,
autoDeleteMergedSegments,
setAutoDeleteMergedSegments,
exportConfirmEnabled,
setExportConfirmEnabled,
segmentsToChapters,
setSegmentsToChapters,
preserveMetadataOnMerge,
setPreserveMetadataOnMerge,
simpleMode,
setSimpleMode,
outSegTemplate,
setOutSegTemplate,
keyboardSeekAccFactor,
setKeyboardSeekAccFactor,
keyboardNormalSeekSpeed,
setKeyboardNormalSeekSpeed,
treatInputFileModifiedTimeAsStart,
setTreatInputFileModifiedTimeAsStart,
treatOutputFileModifiedTimeAsStart,
setTreatOutputFileModifiedTimeAsStart,
outFormatLocked,
setOutFormatLocked,
safeOutputFileName,
setSafeOutputFileName,
enableAutoHtml5ify,
setEnableAutoHtml5ify,
segmentsToChaptersOnly,
setSegmentsToChaptersOnly,
keyBindings,
setKeyBindings,
resetKeyBindings,
enableSmartCut,
setEnableSmartCut,
customFfPath,
setCustomFfPath,
storeProjectInWorkingDir,
setStoreProjectInWorkingDir,
enableOverwriteOutput,
setEnableOverwriteOutput,
mouseWheelZoomModifierKey,
setMouseWheelZoomModifierKey,
captureFrameMethod,
setCaptureFrameMethod,
captureFrameQuality,
setCaptureFrameQuality,
captureFrameFileNameFormat,
setCaptureFrameFileNameFormat,
enableNativeHevc,
setEnableNativeHevc,
enableUpdateCheck,
setEnableUpdateCheck,
cleanupChoices,
setCleanupChoices,
allowMultipleInstances,
setAllowMultipleInstances,
darkMode,
setDarkMode,
preferStrongColors,
setPreferStrongColors,
outputFileNameMinZeroPadding,
setOutputFileNameMinZeroPadding,
cutFromAdjustmentFrames,
setCutFromAdjustmentFrames,
};
};