Fix Settings: stop auto-registering DDNS on cell name change
Unit Tests / test (push) Successful in 7m37s

Two bugs in the pic_ngo availability + auto-save flow:

1. Availability check fired on page load even when cell_name matched
   the currently-registered name — sending unnecessary check requests
   to the DDNS server and showing 'taken' for the user's own name.
   Fix: skip the check when identity.cell_name === loadedCellName.

2. Auto-save triggered DDNS re-registration (release old subdomain +
   register new one) as soon as picAvail became 'available' — without
   the user pressing Accept. This happened because picAvail was in
   the auto-save effect's dependency array, so it re-ran whenever the
   availability check completed.
   Fix: block auto-save entirely for pic_ngo cell name changes; the
   user must press Accept explicitly since re-registration is
   irreversible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 15:09:53 -04:00
parent 9ad9fac8dd
commit 348fd8faad
+7 -9
View File
@@ -458,7 +458,8 @@ function Settings() {
useEffect(() => { useEffect(() => {
if (domainMode !== 'pic_ngo') { setPicAvail(null); return; } if (domainMode !== 'pic_ngo') { setPicAvail(null); return; }
const name = identity.cell_name; const name = identity.cell_name;
if (!name) { setPicAvail(null); return; } // No check needed when the name hasn't changed from what's already registered.
if (!name || name === loadedCellName) { setPicAvail(null); return; }
clearTimeout(picAvailTimerRef.current); clearTimeout(picAvailTimerRef.current);
setPicAvail(null); setPicAvail(null);
picAvailTimerRef.current = setTimeout(async () => { picAvailTimerRef.current = setTimeout(async () => {
@@ -471,7 +472,7 @@ function Settings() {
} }
}, 900); }, 900);
return () => clearTimeout(picAvailTimerRef.current); return () => clearTimeout(picAvailTimerRef.current);
}, [identity.cell_name, domainMode]); // eslint-disable-line react-hooks/exhaustive-deps }, [identity.cell_name, domainMode, loadedCellName]); // eslint-disable-line react-hooks/exhaustive-deps
const saveIdentity = useCallback(async () => { const saveIdentity = useCallback(async () => {
if (ipRangeError || cellNameError || domainError) return; if (ipRangeError || cellNameError || domainError) return;
@@ -649,15 +650,12 @@ function Settings() {
useEffect(() => { useEffect(() => {
if (!identityDirty) return; if (!identityDirty) return;
if (ipRangeError || cellNameError || domainError) return; if (ipRangeError || cellNameError || domainError) return;
// In pic_ngo mode, if the cell name differs from what was last saved/loaded, // pic_ngo cell name changes require DDNS re-registration (irreversible: releases the
// only auto-save once the check confirms the name is available. // old subdomain). Never auto-save these — the user must explicitly press Accept.
// All other states (null, 'checking', 'taken', 'unreachable') block auto-save. if (domainMode === 'pic_ngo' && identity.cell_name !== loadedCellName) return;
if (domainMode === 'pic_ngo' && identity.cell_name !== loadedCellName) {
if (picAvail !== 'available') return;
}
const timer = setTimeout(() => saveIdentityRef.current(), 800); const timer = setTimeout(() => saveIdentityRef.current(), 800);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [identity, identityDirty, ipRangeError, cellNameError, domainError, domainMode, picAvail, loadedCellName]); // eslint-disable-line react-hooks/exhaustive-deps }, [identity, identityDirty, ipRangeError, cellNameError, domainError, domainMode, loadedCellName]); // eslint-disable-line react-hooks/exhaustive-deps
useEffect(() => { useEffect(() => {
const timers = SERVICE_DEFS const timers = SERVICE_DEFS