Skip to content

Commit 297ceef

Browse files
committed
fix(gui): address remaining pool hydration review nits
1 parent fa443c0 commit 297ceef

3 files changed

Lines changed: 38 additions & 14 deletions

File tree

gui/src/clamp-draft.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export function clampNumberDraft(
66
max: number,
77
step = 1,
88
): string {
9-
const parsed = Number(raw);
9+
const trimmed = raw.trim();
10+
const parsed = trimmed === "" ? NaN : Number(trimmed);
1011
const base = Number.isFinite(parsed) ? parsed : min;
1112
const next = Math.min(max, Math.max(min, base + delta));
1213
// Keep one decimal for GiB-style steps; integers otherwise.

gui/src/components/CodexPoolStrategySetting.tsx

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export default function CodexPoolStrategySetting({
4848
const hydratedRef = useRef(false);
4949
const [saving, setSaving] = useState(false);
5050
const savingRef = useRef(false);
51+
/** Set when an /active read arrives mid-save; triggers one post-save refresh. */
52+
const deferredActiveRefreshRef = useRef(false);
5153
const revisionRef = useRef(0);
5254
const [loadError, setLoadError] = useState(false);
5355
const [error, setError] = useState<string | null>(null);
@@ -80,25 +82,47 @@ export default function CodexPoolStrategySetting({
8082
try {
8183
const res = await fetch(`${apiBase}/api/codex-auth/active`);
8284
if (!res.ok) throw new Error("load");
83-
applyServer(await res.json() as {
85+
const payload = await res.json() as {
8486
accountPoolStrategy?: unknown;
8587
accountPoolStickyLimit?: unknown;
86-
});
88+
};
89+
// A save started while this GET was in flight — retry once after it settles.
90+
if (savingRef.current) {
91+
deferredActiveRefreshRef.current = true;
92+
return;
93+
}
94+
applyServer(payload);
8795
} catch {
88-
setLoadError(true);
96+
if (!savingRef.current) setLoadError(true);
8997
}
9098
}, [apiBase, applyServer]);
9199

100+
const scheduleDeferredActiveRefresh = useCallback(() => {
101+
if (!deferredActiveRefreshRef.current) return;
102+
deferredActiveRefreshRef.current = false;
103+
queueMicrotask(() => {
104+
if (savingRef.current) {
105+
deferredActiveRefreshRef.current = true;
106+
return;
107+
}
108+
void load();
109+
});
110+
}, [load]);
111+
92112
// Shared /active observer (preferred): same payload the pool already fetched.
93-
// Ignore stale polls that started before a PUT bumped revision, and never apply while saving.
113+
// Ignore stale polls that started before a PUT bumped revision; mid-save reads
114+
// arm one post-save /active refresh instead of being dropped forever.
94115
// Replay readLastActive on subscribe so late mounts hydrate without waiting a poll.
95116
useEffect(() => {
96117
if (!subscribeLoadObserver) return;
97118
const observer: CodexAccountLoadObserver = {
98119
beginActiveRead: () => revisionRef.current,
99120
acceptActiveRead: (value, startedRevision) => {
100121
if (startedRevision !== revisionRef.current) return;
101-
if (savingRef.current) return;
122+
if (savingRef.current) {
123+
deferredActiveRefreshRef.current = true;
124+
return;
125+
}
102126
applyActivePayload(value);
103127
},
104128
rejectActiveRead: () => {
@@ -154,7 +178,8 @@ export default function CodexPoolStrategySetting({
154178
}
155179
savingRef.current = false;
156180
setSaving(false);
157-
}, [apiBase, stickyLimit, strategy, t]);
181+
scheduleDeferredActiveRefresh();
182+
}, [apiBase, scheduleDeferredActiveRefresh, stickyLimit, strategy, t]);
158183

159184
// Block writes until /active confirms — defaults paint for CLS but must not overwrite server state.
160185
const controlsDisabled = saving || loadError || !hydrated;

gui/src/hooks/useCodexAccountPool.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useCallback, useEffect, useRef, useState } from "react";
2+
import { extractAutoSwitchThresholdPayload } from "../codex-auto-switch";
23
import type { AccountQuota } from "../codex-quota-utils";
34
import { accountNeedsReauth } from "../oauth-health-display";
45

@@ -123,13 +124,10 @@ export function useCodexAccountPool(apiBase: string, enabled = true): CodexAccou
123124
}, []);
124125

125126
/** Last threshold an actual read returned, or undefined when none has succeeded yet. */
126-
const readLastThreshold = useCallback(() => {
127-
const active = lastActiveRef.current?.value;
128-
if (active && typeof active === "object" && active !== null && "autoSwitchThreshold" in active) {
129-
return (active as { autoSwitchThreshold: unknown }).autoSwitchThreshold;
130-
}
131-
return active;
132-
}, []);
127+
const readLastThreshold = useCallback(
128+
() => extractAutoSwitchThresholdPayload(lastActiveRef.current?.value),
129+
[],
130+
);
133131

134132
/** Full last /active payload, or undefined when none has succeeded yet. */
135133
const readLastActive = useCallback(() => lastActiveRef.current?.value, []);

0 commit comments

Comments
 (0)