fix(editing): harden unsaved navigation and draft cleanup

This commit is contained in:
Supra4E8C
2026-02-22 02:51:09 +08:00
parent 8e9235f66c
commit 028568c3ae
4 changed files with 22 additions and 15 deletions
+8 -6
View File
@@ -23,19 +23,21 @@ export function useUnsavedChangesGuard(options: UseUnsavedChangesGuardOptions) {
const { enabled = true, shouldBlock, dialog } = options;
const { showConfirmation } = useNotificationStore();
const lastBlockedRef = useRef<string>('');
const allowNextNavigationRef = useRef(false);
const allowNextNavigationUntilRef = useRef(0);
const location = useLocation();
const allowNextNavigation = useCallback(() => {
allowNextNavigationRef.current = true;
// Allow a short window for programmatic navigations after successful save.
// This avoids stale "allow" flags lingering when no navigation happens.
allowNextNavigationUntilRef.current = Date.now() + 2_000;
}, []);
const shouldBlockFunction = useCallback<BlockerFunction>(
(args) => {
if (allowNextNavigationRef.current) {
if (!enabled) return false;
if (allowNextNavigationUntilRef.current > Date.now()) {
return false;
}
if (!enabled) return false;
return typeof shouldBlock === 'function' ? shouldBlock(args) : shouldBlock;
},
[enabled, shouldBlock]
@@ -44,8 +46,8 @@ export function useUnsavedChangesGuard(options: UseUnsavedChangesGuardOptions) {
const blocker = useBlocker(shouldBlockFunction);
useEffect(() => {
if (!allowNextNavigationRef.current) return;
allowNextNavigationRef.current = false;
if (allowNextNavigationUntilRef.current === 0) return;
allowNextNavigationUntilRef.current = 0;
}, [location.key]);
const blockedKey = useMemo(() => {
+2 -1
View File
@@ -185,6 +185,7 @@ export function AiProvidersCodexEditPage() {
if (initialData) {
const nextForm: ProviderFormState = {
...initialData,
websockets: Boolean(initialData.websockets),
headers: headersToEntries(initialData.headers),
modelEntries: modelsToEntries(initialData.models),
excludedText: excludedModelsToText(initialData.excludedModels),
@@ -362,7 +363,7 @@ export function AiProvidersCodexEditPage() {
priority: form.priority !== undefined ? Math.trunc(form.priority) : undefined,
prefix: form.prefix?.trim() || undefined,
baseUrl,
websockets: form.websockets ?? false,
websockets: Boolean(form.websockets),
proxyUrl: form.proxyUrl?.trim() || undefined,
headers: buildHeaderObject(form.headers),
models: entriesToModels(form.modelEntries),
+6 -4
View File
@@ -196,10 +196,12 @@ export const useClaudeEditDraftStore = create<ClaudeEditDraftState>((set, get) =
clearDraft: (key) => {
if (!key) return;
set((state) => {
if (!state.drafts[key]) return state;
const next = { ...state.drafts };
delete next[key];
return { drafts: next };
if (!state.drafts[key] && !state.refCounts[key]) return state;
const nextDrafts = { ...state.drafts };
delete nextDrafts[key];
const nextCounts = { ...state.refCounts };
delete nextCounts[key];
return { drafts: nextDrafts, refCounts: nextCounts };
});
},
}));
+6 -4
View File
@@ -227,10 +227,12 @@ export const useOpenAIEditDraftStore = create<OpenAIEditDraftState>((set, get) =
clearDraft: (key) => {
if (!key) return;
set((state) => {
if (!state.drafts[key]) return state;
const next = { ...state.drafts };
delete next[key];
return { drafts: next };
if (!state.drafts[key] && !state.refCounts[key]) return state;
const nextDrafts = { ...state.drafts };
delete nextDrafts[key];
const nextCounts = { ...state.refCounts };
delete nextCounts[key];
return { drafts: nextDrafts, refCounts: nextCounts };
});
},
}));