diff --git a/src/components/Inputs/Select/Select.tsx b/src/components/Inputs/Select/Select.tsx index 29039337..e023f78a 100644 --- a/src/components/Inputs/Select/Select.tsx +++ b/src/components/Inputs/Select/Select.tsx @@ -1,5 +1,5 @@ import type { ClassValue } from "clsx"; -import type { ChangeEvent, Dispatch, SetStateAction } from "react"; +import type { ChangeEvent } from "react"; import { useComponentVisible } from "@/hooks"; import { cn } from "@/src/utils/utilities"; @@ -28,11 +28,10 @@ export type SelectProps = { onChange: (value: ChangeEvent) => void; options: SelectOption[]; selectedOption: string | undefined; - setSelectedOption: Dispatch>; title: string; }; -const Select: React.FC = ({ className, disabled, id, label, onChange, options, selectedOption, setSelectedOption }) => { +const Select: React.FC = ({ className, disabled, id, label, onChange, options, selectedOption }) => { const { isComponentVisible: isSelectVisible, ref: selectRef, @@ -44,7 +43,6 @@ const Select: React.FC = ({ className, disabled, id, label, onChang }; const handleOptionSelect = (option: string) => { - setSelectedOption(option); setIsSelectVisible(false); onChange({ currentTarget: { value: option } } as ChangeEvent); }; diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx index d6b86afe..3f4a2d6f 100644 --- a/src/components/Settings/Settings.tsx +++ b/src/components/Settings/Settings.tsx @@ -1,21 +1,11 @@ -import type { - ModifierKey, - OnScreenDisplayColor, - OnScreenDisplayPosition, - OnScreenDisplayType, - ScreenshotFormat, - ScreenshotType, - YoutubePlayerQualityLevel, - configuration, - configurationKeys -} from "@/src/types"; +import type { ModifierKey, configuration, configurationKeys } from "@/src/types"; import type EnUS from "public/locales/en-US.json"; -import type { ChangeEvent, Dispatch, SetStateAction } from "react"; +import type { ChangeEvent, ChangeEventHandler } from "react"; import "@/assets/styles/tailwind.css"; import "@/components/Settings/Settings.css"; import { useNotifications } from "@/hooks"; -import { type AvailableLocales, availableLocales, type i18nInstanceType, i18nService, localeDirection, translationPercentages } from "@/src/i18n"; +import { availableLocales, type i18nInstanceType, i18nService, localeDirection, translationPercentages } from "@/src/i18n"; import { youtubePlayerSpeedRate } from "@/src/types"; import { configurationImportSchema, defaultConfiguration as defaultSettings } from "@/src/utils/constants"; import { cn, parseStoredValue, settingsAreDefault } from "@/src/utils/utilities"; @@ -44,12 +34,10 @@ async function getLanguageOptions() { } function LanguageOptions({ selectedLanguage, - setSelectedLanguage, setValueOption, t }: { selectedLanguage: string | undefined; - setSelectedLanguage: Dispatch>; setValueOption: (key: configurationKeys) => ({ currentTarget }: ChangeEvent) => void; t: i18nInstanceType["t"]; }) { @@ -67,7 +55,6 @@ function LanguageOptions({ onChange={setValueOption("language")} options={languageOptions} selectedOption={selectedLanguage} - setSelectedOption={setSelectedLanguage} title={t("settings.sections.language.select.title")} type="select" /> @@ -76,15 +63,6 @@ function LanguageOptions({ } export default function Settings() { const [settings, setSettings] = useState(undefined); - const [selectedColor, setSelectedColor] = useState(); - const [selectedDisplayType, setSelectedDisplayType] = useState(); - const [selectedDisplayPosition, setSelectedDisplayPosition] = useState(); - const [selectedPlayerQuality, setSelectedPlayerQuality] = useState(); - const [selectedPlayerSpeed, setSelectedPlayerSpeed] = useState(); - const [selectedScreenshotSaveAs, setSelectedScreenshotSaveAs] = useState(); - const [selectedScreenshotFormat, setSelectedScreenshotFormat] = useState(); - const [selectedLanguage, setSelectedLanguage] = useState(); - const [selectedModifierKey, setSelectedModifierKey] = useState(); const [i18nInstance, setI18nInstance] = useState(null); const [firstLoad, setFirstLoad] = useState(true); const settingsImportRef = useRef(null); @@ -99,15 +77,6 @@ export default function Settings() { ).reduce((acc, key) => Object.assign(acc, { [key]: parseStoredValue(settings[key] as string) }), {}); const castedSettings = storedSettings as configuration; setSettings({ ...castedSettings }); - setSelectedColor(castedSettings.osd_display_color); - setSelectedDisplayType(castedSettings.osd_display_type); - setSelectedDisplayPosition(castedSettings.osd_display_position); - setSelectedPlayerQuality(castedSettings.player_quality); - setSelectedPlayerSpeed(castedSettings.player_speed.toString()); - setSelectedScreenshotSaveAs(castedSettings.screenshot_save_as); - setSelectedScreenshotFormat(castedSettings.screenshot_format); - setSelectedLanguage(castedSettings.language); - setSelectedModifierKey(castedSettings.scroll_wheel_volume_control_modifier_key); }); }; @@ -142,32 +111,6 @@ export default function Settings() { JSON.stringify(parsedNewValue) === JSON.stringify(parsedOldValue) ) return; - switch (key) { - case "osd_display_color": - setSelectedColor(parsedNewValue as OnScreenDisplayColor); - break; - case "osd_display_type": - setSelectedDisplayType(parsedNewValue as OnScreenDisplayType); - break; - case "osd_display_position": - setSelectedDisplayPosition(parsedNewValue as OnScreenDisplayPosition); - break; - case "player_quality": - setSelectedPlayerQuality(parsedNewValue as YoutubePlayerQualityLevel); - break; - case "player_speed": - setSelectedPlayerSpeed(parsedNewValue as string); - break; - case "screenshot_save_as": - setSelectedScreenshotSaveAs(parsedNewValue as ScreenshotType); - break; - case "screenshot_format": - setSelectedScreenshotFormat(parsedNewValue as ScreenshotFormat); - break; - case "language": - setSelectedLanguage(parsedNewValue as AvailableLocales); - break; - } setSettings((prevSettings) => { if (prevSettings) { return { ...prevSettings, [key]: parsedNewValue as configuration[typeof key] }; @@ -186,11 +129,13 @@ export default function Settings() { }; }, []); useEffect(() => { - void (async () => { - const instance = await i18nService((selectedLanguage as AvailableLocales) ?? "en-US"); - setI18nInstance(instance); - })(); - }, [selectedLanguage]); + if (settings && settings["language"]) { + void (async () => { + const instance = await i18nService(settings["language"] ?? "en-US"); + setI18nInstance(instance); + })(); + } + }, [settings]); if (!settings || !i18nInstance || (i18nInstance && i18nInstance.isInitialized === false)) { return ; } @@ -207,6 +152,7 @@ export default function Settings() { setFirstLoad(false); setSettings((state) => (state ? { ...state, [key]: value } : undefined)); }; + const getSelectedOption = (key: K) => settings[key]; function saveOptions() { if (settings) { for (const key of Object.keys(settings)) { @@ -369,7 +315,7 @@ export default function Settings() { { label: "4320p", value: "highres" }, { label: "auto", value: "auto" } ].reverse(); - const YouTubePlayerSpeedOptions: SelectOption[] = youtubePlayerSpeedRate.map((rate) => ({ label: rate.toString(), value: rate.toString() })); + const YouTubePlayerSpeedOptions: SelectOption[] = youtubePlayerSpeedRate.map((rate) => ({ label: rate?.toString(), value: rate?.toString() })); const ScreenshotFormatOptions: SelectOption[] = [ { label: "PNG", value: "png" }, { label: "JPEG", value: "jpeg" }, @@ -379,55 +325,53 @@ export default function Settings() { { label: file, value: "file" }, { label: clipboard, value: "clipboard" } ]; - // Import settings from a JSON file. - function importSettings() { - if (settingsImportRef.current === null) return; - - settingsImportRef.current.addEventListener("change", (event) => { - void (async () => { - const { target } = event; - if (!target) return; - const { files } = target as HTMLInputElement; - const file = files?.[0]; - if (file) { - try { - const fileContents = await file.text(); - const importedSettings = JSON.parse(fileContents); - // Validate the imported settings. - const result = configurationImportSchema.safeParse(importedSettings); - if (!result.success) { - const { error } = result; - const errorMessage = generateErrorMessage(error.errors); - window.alert( - t("settings.sections.importExportSettings.importButton.error.validation", { - ERROR_MESSAGE: errorMessage - }) - ); - } else { - const castSettings = importedSettings as configuration; - // Set the imported settings in your state. - setSettings({ ...castSettings }); - for (const key of Object.keys(castSettings)) { - if (typeof castSettings[key] !== "string") { - localStorage.setItem(key, JSON.stringify(castSettings[key])); - void chrome.storage.local.set({ [key]: JSON.stringify(castSettings[key]) }); - } else { - localStorage.setItem(key, castSettings[key] as string); - void chrome.storage.local.set({ [key]: castSettings[key] as string }); - } + const settingsImportChange: ChangeEventHandler = (event): void => { + void (async () => { + const { target } = event; + if (!target) return; + const { files } = target as HTMLInputElement; + const file = files?.[0]; + if (file) { + try { + const fileContents = await file.text(); + const importedSettings = JSON.parse(fileContents); + // Validate the imported settings. + const result = configurationImportSchema.safeParse(importedSettings); + if (!result.success) { + const { error } = result; + const errorMessage = generateErrorMessage(error.errors); + window.alert( + t("settings.sections.importExportSettings.importButton.error.validation", { + ERROR_MESSAGE: errorMessage + }) + ); + } else { + const castSettings = importedSettings as configuration; + // Set the imported settings in your state. + setSettings({ ...castSettings }); + for (const key of Object.keys(castSettings)) { + if (typeof castSettings[key] !== "string") { + localStorage.setItem(key, JSON.stringify(castSettings[key])); + void chrome.storage.local.set({ [key]: JSON.stringify(castSettings[key]) }); + } else { + localStorage.setItem(key, castSettings[key] as string); + void chrome.storage.local.set({ [key]: castSettings[key] as string }); } - // Show a success notification. - addNotification("success", t("settings.sections.importExportSettings.importButton.success")); } - } catch (error) { - // Handle any import errors. - window.alert(t("settings.sections.importExportSettings.importButton.error.unknown")); + // Show a success notification. + addNotification("success", t("settings.sections.importExportSettings.importButton.success")); } + } catch (error) { + // Handle any import errors. + window.alert(t("settings.sections.importExportSettings.importButton.error.unknown")); } - if (settingsImportRef.current) settingsImportRef.current.value = ""; - })(); - }); - + } + if (settingsImportRef.current) settingsImportRef.current.value = ""; + })(); + }; + // Import settings from a JSON file. + function importSettings() { + if (settingsImportRef.current === null) return; // Trigger the file input dialog. settingsImportRef.current.click(); } @@ -475,12 +419,7 @@ export default function Settings() { v{chrome.runtime.getManifest().version} }> - + @@ -568,51 +507,47 @@ export default function Settings() { type="checkbox" /> @@ -686,13 +620,12 @@ export default function Settings() { type="checkbox" /> @@ -708,7 +641,7 @@ export default function Settings() { type="checkbox" /> @@ -813,7 +744,7 @@ export default function Settings() { )} - + ); diff --git a/src/components/Settings/components/Setting.tsx b/src/components/Settings/components/Setting.tsx index e9e45c06..08f2f8d5 100644 --- a/src/components/Settings/components/Setting.tsx +++ b/src/components/Settings/components/Setting.tsx @@ -40,7 +40,7 @@ function SettingInput(settingProps: SettingInputProps) { ); } case "select": { - const { className, disabled, id, label, onChange, options, selectedOption, setSelectedOption, title } = settingProps; + const { className, disabled, id, label, onChange, options, selectedOption, title } = settingProps; return (