Fixed intermittent missing extension override on ui and cleanup (#9575)
Signed-off-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Angie Jones <jones.angie@gmail.com>
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import { AppEvents } from '../../constants/events';
|
|
||||||
import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
import { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||||
import { Puzzle } from 'lucide-react';
|
import { Puzzle } from 'lucide-react';
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '../ui/dropdown-menu';
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '../ui/dropdown-menu';
|
||||||
@@ -16,6 +15,7 @@ import {
|
|||||||
getExtensionOverrides,
|
getExtensionOverrides,
|
||||||
} from '../../store/extensionOverrides';
|
} from '../../store/extensionOverrides';
|
||||||
import { defineMessages, useIntl } from '../../i18n';
|
import { defineMessages, useIntl } from '../../i18n';
|
||||||
|
import { AppEvents } from '../../constants/events';
|
||||||
|
|
||||||
const i18n = defineMessages({
|
const i18n = defineMessages({
|
||||||
manageExtensions: {
|
manageExtensions: {
|
||||||
@@ -68,6 +68,8 @@ interface BottomMenuExtensionSelectionProps {
|
|||||||
sessionId: string | null;
|
sessionId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetSessionExtensionsSignal = Parameters<typeof getSessionExtensions>[0]['signal'];
|
||||||
|
|
||||||
export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionSelectionProps) => {
|
export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionSelectionProps) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
@@ -77,29 +79,26 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS
|
|||||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||||
const [pendingSort, setPendingSort] = useState(false);
|
const [pendingSort, setPendingSort] = useState(false);
|
||||||
const [togglingExtension, setTogglingExtension] = useState<string | null>(null);
|
const [togglingExtension, setTogglingExtension] = useState<string | null>(null);
|
||||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
||||||
const [isSessionExtensionsLoaded, setIsSessionExtensionsLoaded] = useState(false);
|
const [isSessionExtensionsLoaded, setIsSessionExtensionsLoaded] = useState(false);
|
||||||
const sortTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const sortTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
const latestSessionIdRef = useRef(sessionId);
|
||||||
const { extensionsList: allExtensions } = useConfig();
|
const { extensionsList: allExtensions } = useConfig();
|
||||||
const isHubView = !sessionId;
|
const isHubView = !sessionId;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
latestSessionIdRef.current = sessionId;
|
||||||
setIsSessionExtensionsLoaded(false);
|
setIsSessionExtensionsLoaded(false);
|
||||||
setSessionExtensions([]);
|
setSessionExtensions([]);
|
||||||
|
setPendingSort(false);
|
||||||
|
setIsTransitioning(false);
|
||||||
|
setTogglingExtension(null);
|
||||||
|
|
||||||
|
if (sortTimeoutRef.current) {
|
||||||
|
clearTimeout(sortTimeoutRef.current);
|
||||||
|
sortTimeoutRef.current = null;
|
||||||
|
}
|
||||||
}, [sessionId]);
|
}, [sessionId]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleExtensionsLoaded = () => {
|
|
||||||
setRefreshTrigger((prev) => prev + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener(AppEvents.SESSION_EXTENSIONS_LOADED, handleExtensionsLoaded);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener(AppEvents.SESSION_EXTENSIONS_LOADED, handleExtensionsLoaded);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
if (sortTimeoutRef.current) {
|
if (sortTimeoutRef.current) {
|
||||||
@@ -108,33 +107,71 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const loadSessionExtensions = useCallback(
|
||||||
if (refreshTrigger === 0 && !isOpen) {
|
async (targetSessionId: string, signal?: GetSessionExtensionsSignal) => {
|
||||||
return;
|
const response = await getSessionExtensions({
|
||||||
}
|
path: { session_id: targetSessionId },
|
||||||
|
signal,
|
||||||
|
throwOnError: true,
|
||||||
|
});
|
||||||
|
|
||||||
const fetchExtensions = async () => {
|
if (signal?.aborted || latestSessionIdRef.current !== targetSessionId) {
|
||||||
if (!sessionId) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
setSessionExtensions(response.data?.extensions ?? []);
|
||||||
const response = await getSessionExtensions({
|
setIsSessionExtensionsLoaded(true);
|
||||||
path: { session_id: sessionId },
|
},
|
||||||
});
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
if (response.data?.extensions) {
|
useEffect(() => {
|
||||||
setSessionExtensions(response.data.extensions);
|
if (!sessionId) {
|
||||||
setIsSessionExtensionsLoaded(true);
|
setIsSessionExtensionsLoaded(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller: AbortController | null = null;
|
||||||
|
|
||||||
|
const loadExtensionsForCurrentSession = (event: Event) => {
|
||||||
|
const targetSessionId = (event as CustomEvent<{ sessionId?: string }>).detail?.sessionId;
|
||||||
|
|
||||||
|
if (targetSessionId !== sessionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
controller?.abort();
|
||||||
|
const currentController = new AbortController();
|
||||||
|
controller = currentController;
|
||||||
|
|
||||||
|
loadSessionExtensions(targetSessionId, currentController.signal).catch((error) => {
|
||||||
|
if (currentController.signal.aborted || latestSessionIdRef.current !== targetSessionId) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch session extensions:', error);
|
console.error('Failed to fetch session extensions:', error);
|
||||||
setIsSessionExtensionsLoaded(true);
|
setIsSessionExtensionsLoaded(true);
|
||||||
}
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchExtensions();
|
window.addEventListener(AppEvents.SESSION_EXTENSIONS_LOADED, loadExtensionsForCurrentSession);
|
||||||
}, [sessionId, isOpen, refreshTrigger]);
|
|
||||||
|
return () => {
|
||||||
|
controller?.abort();
|
||||||
|
window.removeEventListener(
|
||||||
|
AppEvents.SESSION_EXTENSIONS_LOADED,
|
||||||
|
loadExtensionsForCurrentSession
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}, [sessionId, loadSessionExtensions]);
|
||||||
|
|
||||||
|
const finishSessionTransition = useCallback((targetSessionId: string) => {
|
||||||
|
if (latestSessionIdRef.current === targetSessionId) {
|
||||||
|
setPendingSort(false);
|
||||||
|
setIsTransitioning(false);
|
||||||
|
setTogglingExtension(null);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleToggle = useCallback(
|
const handleToggle = useCallback(
|
||||||
async (extensionConfig: FixedExtensionEntry) => {
|
async (extensionConfig: FixedExtensionEntry) => {
|
||||||
@@ -160,6 +197,7 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS
|
|||||||
setPendingSort(false);
|
setPendingSort(false);
|
||||||
setIsTransitioning(false);
|
setIsTransitioning(false);
|
||||||
setTogglingExtension(null);
|
setTogglingExtension(null);
|
||||||
|
sortTimeoutRef.current = null;
|
||||||
}, 800);
|
}, 800);
|
||||||
|
|
||||||
toastService.success({
|
toastService.success({
|
||||||
@@ -196,17 +234,17 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS
|
|||||||
clearTimeout(sortTimeoutRef.current);
|
clearTimeout(sortTimeoutRef.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
sortTimeoutRef.current = setTimeout(async () => {
|
sortTimeoutRef.current = setTimeout(() => {
|
||||||
const response = await getSessionExtensions({
|
loadSessionExtensions(sessionId)
|
||||||
path: { session_id: sessionId },
|
.catch((error) => {
|
||||||
});
|
if (latestSessionIdRef.current === sessionId) {
|
||||||
|
console.error('Failed to fetch session extensions:', error);
|
||||||
if (response.data?.extensions) {
|
}
|
||||||
setSessionExtensions(response.data.extensions);
|
})
|
||||||
}
|
.finally(() => {
|
||||||
setPendingSort(false);
|
finishSessionTransition(sessionId);
|
||||||
setIsTransitioning(false);
|
sortTimeoutRef.current = null;
|
||||||
setTogglingExtension(null);
|
});
|
||||||
}, 800);
|
}, 800);
|
||||||
} catch {
|
} catch {
|
||||||
setIsTransitioning(false);
|
setIsTransitioning(false);
|
||||||
@@ -214,7 +252,7 @@ export const BottomMenuExtensionSelection = ({ sessionId }: BottomMenuExtensionS
|
|||||||
setTogglingExtension(null);
|
setTogglingExtension(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[sessionId, isHubView, togglingExtension, intl]
|
[sessionId, isHubView, togglingExtension, intl, loadSessionExtensions, finishSessionTransition]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Merge all available extensions with session-specific or hub override state
|
// Merge all available extensions with session-specific or hub override state
|
||||||
|
|||||||
@@ -748,7 +748,9 @@ export function useChatStream({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
window.dispatchEvent(new CustomEvent(AppEvents.SESSION_EXTENSIONS_LOADED));
|
window.dispatchEvent(
|
||||||
|
new CustomEvent(AppEvents.SESSION_EXTENSIONS_LOADED, { detail: { sessionId } })
|
||||||
|
);
|
||||||
onSessionLoaded?.();
|
onSessionLoaded?.();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -776,7 +778,9 @@ export function useChatStream({
|
|||||||
const extensionResults = resumeData?.extension_results;
|
const extensionResults = resumeData?.extension_results;
|
||||||
|
|
||||||
showExtensionLoadResults(extensionResults);
|
showExtensionLoadResults(extensionResults);
|
||||||
window.dispatchEvent(new CustomEvent(AppEvents.SESSION_EXTENSIONS_LOADED));
|
window.dispatchEvent(
|
||||||
|
new CustomEvent(AppEvents.SESSION_EXTENSIONS_LOADED, { detail: { sessionId } })
|
||||||
|
);
|
||||||
|
|
||||||
const pendingRequestId = pendingReattachRequestIdRef.current;
|
const pendingRequestId = pendingReattachRequestIdRef.current;
|
||||||
const reattachedToActiveRequest = activeRequestIdRef.current !== null;
|
const reattachedToActiveRequest = activeRequestIdRef.current !== null;
|
||||||
|
|||||||
Reference in New Issue
Block a user