fix(system): preserve MQTT interval draft editing

This commit is contained in:
DCCONSTRUCTIONS
2026-07-28 01:56:36 +03:00
parent d2964a0ddc
commit 65a72d40a8
4 changed files with 185 additions and 19 deletions
@@ -18,6 +18,11 @@ import {
type ComputeContourDraft,
type ComputeContourPlatform,
} from "../../core/system/computeContours";
import {
DEFAULT_TELEMETRY_POLL_INTERVAL_SECONDS,
parseTelemetryPollIntervalDraft,
resolveTelemetryPollIntervalDraft,
} from "../../core/system/telemetryPollInterval";
interface ComputeContourSettingsWindowProps {
open: boolean;
@@ -51,7 +56,7 @@ function emptyDraft(): ComputeContourDraft {
ssh_port: 22,
mqtt_host: "127.0.0.1",
mqtt_port: 1883,
telemetry_poll_interval_seconds: 3,
telemetry_poll_interval_seconds: DEFAULT_TELEMETRY_POLL_INTERVAL_SECONDS,
};
}
@@ -79,6 +84,9 @@ export function ComputeContourSettingsWindow({
onUpdate,
}: ComputeContourSettingsWindowProps) {
const [draft, setDraft] = useState<ComputeContourDraft>(() => draftFromContour(contour));
const [telemetryPollIntervalDraft, setTelemetryPollIntervalDraft] = useState(
() => String(draftFromContour(contour).telemetry_poll_interval_seconds),
);
const [activeSection, setActiveSection] = useState<"connection" | "agent">("connection");
const [install, setInstall] = useState<ComputeContourAgentInstall | null>(null);
const [busy, setBusy] = useState(false);
@@ -86,7 +94,9 @@ export function ComputeContourSettingsWindow({
useEffect(() => {
if (!open) return;
setDraft(draftFromContour(mode === "edit" ? contour : null));
const nextDraft = draftFromContour(mode === "edit" ? contour : null);
setDraft(nextDraft);
setTelemetryPollIntervalDraft(String(nextDraft.telemetry_poll_interval_seconds));
setActiveSection("connection");
setInstall(null);
setError(null);
@@ -107,6 +117,11 @@ export function ComputeContourSettingsWindow({
return () => controller.abort();
}, [contour, mode, open]);
const telemetryPollIntervalSeconds = useMemo(
() => parseTelemetryPollIntervalDraft(telemetryPollIntervalDraft),
[telemetryPollIntervalDraft],
);
const valid = useMemo(() => (
Boolean(draft.display_name.trim())
&& Boolean(draft.expected_node_id.trim())
@@ -114,20 +129,39 @@ export function ComputeContourSettingsWindow({
&& draft.ssh_port > 0
&& Number.isInteger(draft.mqtt_port)
&& draft.mqtt_port > 0
&& Number.isInteger(draft.telemetry_poll_interval_seconds)
&& draft.telemetry_poll_interval_seconds >= 1
&& draft.telemetry_poll_interval_seconds <= 60
), [draft]);
&& telemetryPollIntervalSeconds !== null
), [draft, telemetryPollIntervalSeconds]);
const commitTelemetryPollInterval = () => {
const resolution = resolveTelemetryPollIntervalDraft(
telemetryPollIntervalDraft,
draft.telemetry_poll_interval_seconds,
);
setTelemetryPollIntervalDraft(resolution.draft);
if (!resolution.accepted) return;
setDraft((current) => (
current.telemetry_poll_interval_seconds === resolution.seconds
? current
: {
...current,
telemetry_poll_interval_seconds: resolution.seconds,
}
));
};
const save = async () => {
if (!valid || busy) return;
if (!valid || busy || telemetryPollIntervalSeconds === null) return;
const committedDraft = {
...draft,
telemetry_poll_interval_seconds: telemetryPollIntervalSeconds,
};
setBusy(true);
setError(null);
try {
if (mode === "edit" && contour) {
await onUpdate(contour, draft);
await onUpdate(contour, committedDraft);
} else {
await onCreate(draft);
await onCreate(committedDraft);
}
onClose();
} catch (reason) {
@@ -241,14 +275,26 @@ export function ComputeContourSettingsWindow({
<TextField
label="Интервал MQTT"
hint="1–60 с"
type="number"
min={1}
max={60}
value={String(draft.telemetry_poll_interval_seconds)}
onChange={(event) => setDraft((current) => ({
...current,
telemetry_poll_interval_seconds: Number(event.currentTarget.value),
}))}
type="text"
inputMode="numeric"
autoComplete="off"
aria-invalid={telemetryPollIntervalSeconds === null}
value={telemetryPollIntervalDraft}
onChange={(event) => setTelemetryPollIntervalDraft(event.currentTarget.value)}
onBlur={commitTelemetryPollInterval}
onKeyDown={(event) => {
if (event.key === "Enter") {
event.preventDefault();
event.stopPropagation();
commitTelemetryPollInterval();
} else if (event.key === "Escape") {
event.preventDefault();
event.stopPropagation();
setTelemetryPollIntervalDraft(
String(draft.telemetry_poll_interval_seconds),
);
}
}}
/>
{draft.telemetry_mode === "legacy-ssh" ? (
<TextField