Fix UI lint warnings

There were a bunch of ref and useEffect violations.
This commit is contained in:
Marc Brooks 2025-10-13 20:11:58 -05:00
parent 0eb577b6f7
commit 0dcf56ef18
No known key found for this signature in database
GPG Key ID: 583A6AF2D6AE1DC6
4 changed files with 73 additions and 77 deletions

View File

@ -41,8 +41,6 @@ export default function SettingsGeneralUpdateRoute() {
return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />; return <Dialog onClose={() => navigate("..")} onConfirmUpdate={onConfirmUpdate} />;
} }
export function Dialog({ export function Dialog({
onClose, onClose,
onConfirmUpdate, onConfirmUpdate,
@ -71,11 +69,6 @@ export function Dialog({
[setModalView], [setModalView],
); );
// Reset modal view when dialog is opened
useEffect(() => {
setVersionInfo(null);
}, [setModalView]);
return ( return (
<div className="pointer-events-auto relative mx-auto text-left"> <div className="pointer-events-auto relative mx-auto text-left">
<div> <div>
@ -133,8 +126,6 @@ function LoadingState({
const progressBarRef = useRef<HTMLDivElement>(null); const progressBarRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
setProgressWidth("0%");
abortControllerRef.current = new AbortController(); abortControllerRef.current = new AbortController();
const signal = abortControllerRef.current.signal; const signal = abortControllerRef.current.signal;

View File

@ -1,20 +1,19 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { BacklightSettings, useSettingsStore } from "@hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import { FeatureFlag } from "@components/FeatureFlag";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { SettingsItem } from "@components/SettingsItem"; import { SettingsItem } from "@components/SettingsItem";
import { SettingsPageHeader } from "@components/SettingsPageheader"; import { SettingsPageHeader } from "@components/SettingsPageheader";
import { BacklightSettings, useSettingsStore } from "@/hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc";
import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { UsbDeviceSetting } from "@components/UsbDeviceSetting"; import { UsbDeviceSetting } from "@components/UsbDeviceSetting";
import { UsbInfoSetting } from "@components/UsbInfoSetting";
import notifications from "../notifications"; import notifications from "@/notifications";
import { UsbInfoSetting } from "../components/UsbInfoSetting";
import { FeatureFlag } from "../components/FeatureFlag";
export default function SettingsHardwareRoute() { export default function SettingsHardwareRoute() {
const { send } = useJsonRpc(); const { send } = useJsonRpc();
const settings = useSettingsStore(); const settings = useSettingsStore();
const { setDisplayRotation } = useSettingsStore(); const { displayRotation, setDisplayRotation } = useSettingsStore();
const handleDisplayRotationChange = (rotation: string) => { const handleDisplayRotationChange = (rotation: string) => {
setDisplayRotation(rotation); setDisplayRotation(rotation);
@ -22,7 +21,7 @@ export default function SettingsHardwareRoute() {
}; };
const handleDisplayRotationSave = () => { const handleDisplayRotationSave = () => {
send("setDisplayRotation", { params: { rotation: settings.displayRotation } }, (resp: JsonRpcResponse) => { send("setDisplayRotation", { params: { rotation: displayRotation } }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to set display orientation: ${resp.error.data || "Unknown error"}`, `Failed to set display orientation: ${resp.error.data || "Unknown error"}`,
@ -33,7 +32,7 @@ export default function SettingsHardwareRoute() {
}); });
}; };
const { setBacklightSettings } = useSettingsStore(); const { backlightSettings, setBacklightSettings } = useSettingsStore();
const handleBacklightSettingsChange = (settings: BacklightSettings) => { const handleBacklightSettingsChange = (settings: BacklightSettings) => {
// If the user has set the display to dim after it turns off, set the dim_after // If the user has set the display to dim after it turns off, set the dim_after
@ -47,7 +46,7 @@ export default function SettingsHardwareRoute() {
}; };
const handleBacklightSettingsSave = () => { const handleBacklightSettingsSave = () => {
send("setBacklightSettings", { params: settings.backlightSettings }, (resp: JsonRpcResponse) => { send("setBacklightSettings", { params: backlightSettings }, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
notifications.error( notifications.error(
`Failed to set backlight settings: ${resp.error.data || "Unknown error"}`, `Failed to set backlight settings: ${resp.error.data || "Unknown error"}`,
@ -58,6 +57,21 @@ export default function SettingsHardwareRoute() {
}); });
}; };
const handleBacklightMaxBrightnessChange = (max_brightness: number) => {
const settings = { ...backlightSettings, max_brightness };
handleBacklightSettingsChange(settings);
};
const handleBacklightDimAfterChange = (dim_after: number) => {
const settings = { ...backlightSettings, dim_after };
handleBacklightSettingsChange(settings);
};
const handleBacklightOffAfterChange = (off_after: number) => {
const settings = { ...backlightSettings, off_after };
handleBacklightSettingsChange(settings);
};
useEffect(() => { useEffect(() => {
send("getBacklightSettings", {}, (resp: JsonRpcResponse) => { send("getBacklightSettings", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) { if ("error" in resp) {
@ -90,8 +104,7 @@ export default function SettingsHardwareRoute() {
{ value: "90", label: "Inverted" }, { value: "90", label: "Inverted" },
]} ]}
onChange={e => { onChange={e => {
settings.displayRotation = e.target.value; handleDisplayRotationChange(e.target.value);
handleDisplayRotationChange(settings.displayRotation);
}} }}
/> />
</SettingsItem> </SettingsItem>
@ -102,7 +115,7 @@ export default function SettingsHardwareRoute() {
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM"
label="" label=""
value={settings.backlightSettings.max_brightness.toString()} value={backlightSettings.max_brightness.toString()}
options={[ options={[
{ value: "0", label: "Off" }, { value: "0", label: "Off" },
{ value: "10", label: "Low" }, { value: "10", label: "Low" },
@ -110,12 +123,11 @@ export default function SettingsHardwareRoute() {
{ value: "64", label: "High" }, { value: "64", label: "High" },
]} ]}
onChange={e => { onChange={e => {
settings.backlightSettings.max_brightness = parseInt(e.target.value); handleBacklightMaxBrightnessChange(parseInt(e.target.value));
handleBacklightSettingsChange(settings.backlightSettings);
}} }}
/> />
</SettingsItem> </SettingsItem>
{settings.backlightSettings.max_brightness != 0 && ( {backlightSettings.max_brightness != 0 && (
<> <>
<SettingsItem <SettingsItem
title="Dim Display After" title="Dim Display After"
@ -124,7 +136,7 @@ export default function SettingsHardwareRoute() {
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM"
label="" label=""
value={settings.backlightSettings.dim_after.toString()} value={backlightSettings.dim_after.toString()}
options={[ options={[
{ value: "0", label: "Never" }, { value: "0", label: "Never" },
{ value: "60", label: "1 Minute" }, { value: "60", label: "1 Minute" },
@ -134,8 +146,7 @@ export default function SettingsHardwareRoute() {
{ value: "3600", label: "1 Hour" }, { value: "3600", label: "1 Hour" },
]} ]}
onChange={e => { onChange={e => {
settings.backlightSettings.dim_after = parseInt(e.target.value); handleBacklightDimAfterChange(parseInt(e.target.value));
handleBacklightSettingsChange(settings.backlightSettings);
}} }}
/> />
</SettingsItem> </SettingsItem>
@ -146,7 +157,7 @@ export default function SettingsHardwareRoute() {
<SelectMenuBasic <SelectMenuBasic
size="SM" size="SM"
label="" label=""
value={settings.backlightSettings.off_after.toString()} value={backlightSettings.off_after.toString()}
options={[ options={[
{ value: "0", label: "Never" }, { value: "0", label: "Never" },
{ value: "300", label: "5 Minutes" }, { value: "300", label: "5 Minutes" },
@ -155,8 +166,7 @@ export default function SettingsHardwareRoute() {
{ value: "3600", label: "1 Hour" }, { value: "3600", label: "1 Hour" },
]} ]}
onChange={e => { onChange={e => {
settings.backlightSettings.off_after = parseInt(e.target.value); handleBacklightOffAfterChange(parseInt(e.target.value));
handleBacklightSettingsChange(settings.backlightSettings);
}} }}
/> />
</SettingsItem> </SettingsItem>

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import dayjs from "dayjs"; import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from "dayjs/plugin/relativeTime";
import { LuEthernetPort } from "react-icons/lu"; import { LuEthernetPort } from "react-icons/lu";
@ -12,23 +12,22 @@ import {
NetworkState, NetworkState,
TimeSyncMode, TimeSyncMode,
useNetworkStateStore, useNetworkStateStore,
} from "@/hooks/stores"; } from "@hooks/stores";
import { JsonRpcResponse, useJsonRpc } from "@/hooks/useJsonRpc"; import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
import AutoHeight from "@components/AutoHeight";
import { Button } from "@components/Button"; import { Button } from "@components/Button";
import { ConfirmDialog } from "@components/ConfirmDialog";
import EmptyCard from "@components/EmptyCard";
import Fieldset from "@components/Fieldset";
import { GridCard } from "@components/Card"; import { GridCard } from "@components/Card";
import InputField, { InputFieldWithLabel } from "@components/InputField"; import InputField, { InputFieldWithLabel } from "@components/InputField";
import { SelectMenuBasic } from "@/components/SelectMenuBasic"; import Ipv6NetworkCard from "@components/Ipv6NetworkCard";
import { SettingsPageHeader } from "@/components/SettingsPageheader"; import DhcpLeaseCard from "@components/DhcpLeaseCard";
import Fieldset from "@/components/Fieldset"; import { SelectMenuBasic } from "@components/SelectMenuBasic";
import { ConfirmDialog } from "@/components/ConfirmDialog";
import { SettingsItem } from "@components/SettingsItem"; import { SettingsItem } from "@components/SettingsItem";
import { SettingsPageHeader } from "@components/SettingsPageheader";
import notifications from "@/notifications"; import notifications from "@/notifications";
import Ipv6NetworkCard from "../components/Ipv6NetworkCard";
import EmptyCard from "../components/EmptyCard";
import AutoHeight from "../components/AutoHeight";
import DhcpLeaseCard from "../components/DhcpLeaseCard";
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const defaultNetworkSettings: NetworkSettings = { const defaultNetworkSettings: NetworkSettings = {
@ -46,14 +45,18 @@ const defaultNetworkSettings: NetworkSettings = {
export function LifeTimeLabel({ lifetime }: { lifetime: string }) { export function LifeTimeLabel({ lifetime }: { lifetime: string }) {
const [remaining, setRemaining] = useState<string | null>(null); const [remaining, setRemaining] = useState<string | null>(null);
useEffect(() => { const updateRemaining = useCallback(() => {
setRemaining(dayjs(lifetime).fromNow()); setRemaining(dayjs(lifetime).fromNow());
}, [lifetime]);
useEffect(() => {
setTimeout(() => updateRemaining(), 0);
const interval = setInterval(() => { const interval = setInterval(() => {
setRemaining(dayjs(lifetime).fromNow()); updateRemaining();
}, 1000 * 30); }, 1000 * 30);
return () => clearInterval(interval); return () => clearInterval(interval);
}, [lifetime]); }, [updateRemaining]);
if (lifetime == "") { if (lifetime == "") {
return <strong>N/A</strong>; return <strong>N/A</strong>;
@ -81,24 +84,19 @@ export default function SettingsNetworkRoute() {
useState<NetworkSettings>(defaultNetworkSettings); useState<NetworkSettings>(defaultNetworkSettings);
// We use this to determine whether the settings have changed // We use this to determine whether the settings have changed
const firstNetworkSettings = useRef<NetworkSettings | undefined>(undefined); const [firstNetworkSettings, setFirstNetworkSettings] = useState<NetworkSettings | undefined>(undefined);
const [networkSettingsLoaded, setNetworkSettingsLoaded] = useState(false); const [networkSettingsLoaded, setNetworkSettingsLoaded] = useState(false);
const [customDomain, setCustomDomain] = useState<string>(""); const selectedDomainOption = useMemo(() => {
const [selectedDomainOption, setSelectedDomainOption] = useState<string>("dhcp"); if (!networkSettingsLoaded) return "dhcp";
useEffect(() => {
if (networkSettings.domain && networkSettingsLoaded) {
// Check if the domain is one of the predefined options
const predefinedOptions = ["dhcp", "local"]; const predefinedOptions = ["dhcp", "local"];
if (predefinedOptions.includes(networkSettings.domain)) { return predefinedOptions.includes(networkSettings.domain) ? networkSettings.domain : "custom";
setSelectedDomainOption(networkSettings.domain); }, [networkSettings.domain, networkSettingsLoaded]);
} else {
setSelectedDomainOption("custom"); const customDomain = useMemo(() => {
setCustomDomain(networkSettings.domain); if (!networkSettingsLoaded) return "";
} const predefinedOptions = ["dhcp", "local"];
} return predefinedOptions.includes(networkSettings.domain) ? "" : networkSettings.domain;
}, [networkSettings.domain, networkSettingsLoaded]); }, [networkSettings.domain, networkSettingsLoaded]);
const getNetworkSettings = useCallback(() => { const getNetworkSettings = useCallback(() => {
@ -109,12 +107,12 @@ export default function SettingsNetworkRoute() {
console.debug("Network settings: ", networkSettings); console.debug("Network settings: ", networkSettings);
setNetworkSettings(networkSettings); setNetworkSettings(networkSettings);
if (!firstNetworkSettings.current) { if (!firstNetworkSettings) {
firstNetworkSettings.current = networkSettings; setFirstNetworkSettings(networkSettings);
} }
setNetworkSettingsLoaded(true); setNetworkSettingsLoaded(true);
}); });
}, [send]); }, [send, firstNetworkSettings]);
const getNetworkState = useCallback(() => { const getNetworkState = useCallback(() => {
send("getNetworkState", {}, (resp: JsonRpcResponse) => { send("getNetworkState", {}, (resp: JsonRpcResponse) => {
@ -138,8 +136,7 @@ export default function SettingsNetworkRoute() {
return; return;
} }
const networkSettings = resp.result as NetworkSettings; const networkSettings = resp.result as NetworkSettings;
// We need to update the firstNetworkSettings ref to the new settings so we can use it to determine if the settings have changed setFirstNetworkSettings(networkSettings);
firstNetworkSettings.current = networkSettings;
setNetworkSettings(networkSettings); setNetworkSettings(networkSettings);
getNetworkState(); getNetworkState();
setNetworkSettingsLoaded(true); setNetworkSettingsLoaded(true);
@ -160,8 +157,10 @@ export default function SettingsNetworkRoute() {
}, [send]); }, [send]);
useEffect(() => { useEffect(() => {
setTimeout(() => {
getNetworkState(); getNetworkState();
getNetworkSettings(); getNetworkSettings();
}, 0);
}, [getNetworkState, getNetworkSettings]); }, [getNetworkState, getNetworkSettings]);
const handleIpv4ModeChange = (value: IPv4Mode | string) => { const handleIpv4ModeChange = (value: IPv4Mode | string) => {
@ -197,14 +196,12 @@ export default function SettingsNetworkRoute() {
}; };
const handleDomainOptionChange = (value: string) => { const handleDomainOptionChange = (value: string) => {
setSelectedDomainOption(value);
if (value !== "custom") { if (value !== "custom") {
handleDomainChange(value); handleDomainChange(value);
} }
}; };
const handleCustomDomainChange = (value: string) => { const handleCustomDomainChange = (value: string) => {
setCustomDomain(value);
handleDomainChange(value); handleDomainChange(value);
}; };
@ -309,7 +306,6 @@ export default function SettingsNetworkRoute() {
placeholder="home" placeholder="home"
value={customDomain} value={customDomain}
onChange={e => { onChange={e => {
setCustomDomain(e.target.value);
handleCustomDomainChange(e.target.value); handleCustomDomainChange(e.target.value);
}} }}
/> />
@ -361,7 +357,7 @@ export default function SettingsNetworkRoute() {
<Button <Button
size="SM" size="SM"
theme="primary" theme="primary"
disabled={firstNetworkSettings.current === networkSettings} disabled={firstNetworkSettings === networkSettings}
text="Save Settings" text="Save Settings"
onClick={() => setNetworkSettingsRemote(networkSettings)} onClick={() => setNetworkSettingsRemote(networkSettings)}
/> />

View File

@ -50,7 +50,7 @@ export default function SettingsVideoRoute() {
const [streamQuality, setStreamQuality] = useState("1"); const [streamQuality, setStreamQuality] = useState("1");
const [customEdidValue, setCustomEdidValue] = useState<string | null>(null); const [customEdidValue, setCustomEdidValue] = useState<string | null>(null);
const [edid, setEdid] = useState<string | null>(null); const [edid, setEdid] = useState<string | null>(null);
const [edidLoading, setEdidLoading] = useState(false); const [edidLoading, setEdidLoading] = useState(true);
const { debugMode } = useSettingsStore(); const { debugMode } = useSettingsStore();
// Video enhancement settings from store // Video enhancement settings from store
const { const {
@ -63,7 +63,6 @@ export default function SettingsVideoRoute() {
} = useSettingsStore(); } = useSettingsStore();
useEffect(() => { useEffect(() => {
setEdidLoading(true);
send("getStreamQualityFactor", {}, (resp: JsonRpcResponse) => { send("getStreamQualityFactor", {}, (resp: JsonRpcResponse) => {
if ("error" in resp) return; if ("error" in resp) return;
setStreamQuality(String(resp.result)); setStreamQuality(String(resp.result));