fix: extension list not refreshing after installing from deeplink (#3878)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { Button } from '../../ui/button';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { GPSIcon } from '../../ui/icons';
|
||||
@@ -33,8 +33,7 @@ export default function ExtensionsSection({
|
||||
customToggle,
|
||||
selectedExtensions = [],
|
||||
}: ExtensionSectionProps) {
|
||||
const { getExtensions, addExtension, removeExtension } = useConfig();
|
||||
const [extensions, setExtensions] = useState<FixedExtensionEntry[]>([]);
|
||||
const { getExtensions, addExtension, removeExtension, extensionsList } = useConfig();
|
||||
const [selectedExtension, setSelectedExtension] = useState<FixedExtensionEntry | null>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
|
||||
@@ -51,19 +50,11 @@ export default function ExtensionsSection({
|
||||
setShowEnvVarsStateVar(showEnvVars);
|
||||
}, [deepLinkConfig, showEnvVars]);
|
||||
|
||||
// Reset deep link state when component is re-mounted (via key prop changes)
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// Cleanup function to reset state when component unmounts
|
||||
setDeepLinkConfigStateVar(null);
|
||||
setShowEnvVarsStateVar(null);
|
||||
};
|
||||
}, []);
|
||||
// Process extensions from context - this automatically updates when extensionsList changes
|
||||
const extensions = useMemo(() => {
|
||||
if (extensionsList.length === 0) return [];
|
||||
|
||||
const fetchExtensions = useCallback(async () => {
|
||||
const extensionsList = await getExtensions(true); // Force refresh
|
||||
// Sort extensions by name to maintain consistent order
|
||||
const sortedExtensions = [...extensionsList]
|
||||
return [...extensionsList]
|
||||
.sort((a, b) => {
|
||||
// First sort by builtin
|
||||
if (a.type === 'builtin' && b.type !== 'builtin') return -1;
|
||||
@@ -83,24 +74,15 @@ export default function ExtensionsSection({
|
||||
// Use selectedExtensions to determine enabled state in recipe editor
|
||||
enabled: disableConfiguration ? selectedExtensions.includes(ext.name) : ext.enabled,
|
||||
}));
|
||||
}, [extensionsList, disableConfiguration, selectedExtensions]);
|
||||
|
||||
setExtensions(sortedExtensions);
|
||||
}, [getExtensions, disableConfiguration, selectedExtensions]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchExtensions();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const fetchExtensions = useCallback(async () => {
|
||||
await getExtensions(true); // Force refresh - this will update the context
|
||||
}, [getExtensions]);
|
||||
|
||||
const handleExtensionToggle = async (extension: FixedExtensionEntry) => {
|
||||
if (customToggle) {
|
||||
await customToggle(extension);
|
||||
// After custom toggle, update the local state to reflect the change
|
||||
setExtensions((prevExtensions) =>
|
||||
prevExtensions.map((ext) =>
|
||||
ext.name === extension.name ? { ...ext, enabled: !ext.enabled } : ext
|
||||
)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,18 +14,20 @@ interface ApiResponse {
|
||||
export async function extensionApiCall(
|
||||
endpoint: string,
|
||||
payload: ExtensionConfig | string,
|
||||
options: ToastServiceOptions = {}
|
||||
options: ToastServiceOptions & { isDelete?: boolean } = {}
|
||||
): Promise<Response> {
|
||||
// Configure toast notifications
|
||||
toastService.configure(options);
|
||||
|
||||
// Determine if we're activating or removing an extension
|
||||
// Determine if we're activating, deactivating, or removing an extension
|
||||
const isActivating = endpoint == '/extensions/add';
|
||||
const isRemoving = options.isDelete === true;
|
||||
|
||||
const action = {
|
||||
type: isActivating ? 'activating' : 'removing',
|
||||
verb: isActivating ? 'Activating' : 'Removing',
|
||||
pastTense: isActivating ? 'activated' : 'removed',
|
||||
presentTense: isActivating ? 'activate' : 'remove',
|
||||
type: isActivating ? 'activating' : isRemoving ? 'removing' : 'deactivating',
|
||||
verb: isActivating ? 'Activating' : isRemoving ? 'Removing' : 'Deactivating',
|
||||
pastTense: isActivating ? 'activated' : isRemoving ? 'removed' : 'deactivated',
|
||||
presentTense: isActivating ? 'activate' : isRemoving ? 'remove' : 'deactivate',
|
||||
};
|
||||
|
||||
// for adding the payload is an extensionConfig, for removing payload is just the name
|
||||
@@ -71,7 +73,7 @@ export async function extensionApiCall(
|
||||
toastService.dismiss(toastId);
|
||||
toastService.success({
|
||||
title: extensionName,
|
||||
msg: `Successfully ${action.pastTense} extension!`,
|
||||
msg: `Successfully ${action.pastTense} extension`,
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
@@ -114,7 +116,7 @@ function handleErrorResponse(
|
||||
}
|
||||
|
||||
// General error case
|
||||
const msg = `Failed to ${action.type === 'activating' ? 'add' : 'remove'} ${extensionName} extension: ${errorMsg}`;
|
||||
const msg = `Failed to ${action.type === 'activating' ? 'add' : action.type === 'removing' ? 'remove' : 'deactivate'} ${extensionName} extension: ${errorMsg}`;
|
||||
toastService.dismiss(toastId);
|
||||
toastService.error({
|
||||
title: extensionName,
|
||||
@@ -168,12 +170,13 @@ export async function addToAgent(
|
||||
*/
|
||||
export async function removeFromAgent(
|
||||
name: string,
|
||||
options: ToastServiceOptions = {}
|
||||
options: ToastServiceOptions & { isDelete?: boolean } = {}
|
||||
): Promise<Response> {
|
||||
try {
|
||||
return await extensionApiCall('/extensions/remove', sanitizeName(name), options);
|
||||
} catch (error) {
|
||||
console.error(`Failed to remove extension ${name} from agent:`, error);
|
||||
const action = options.isDelete ? 'remove' : 'deactivate';
|
||||
console.error(`Failed to ${action} extension ${name} from agent:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,7 +336,7 @@ export async function deleteExtension({ name, removeFromConfig }: DeleteExtensio
|
||||
// remove from agent
|
||||
let agentRemoveError = null;
|
||||
try {
|
||||
await removeFromAgent(name);
|
||||
await removeFromAgent(name, { isDelete: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to remove extension from agent during deletion:', error);
|
||||
agentRemoveError = error;
|
||||
|
||||
Reference in New Issue
Block a user