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