ui: toggle back off after failure (#1911)
This commit is contained in:
@@ -49,14 +49,22 @@ export default function ExtensionsSection() {
|
|||||||
const toggleDirection = extension.enabled ? 'toggleOff' : 'toggleOn';
|
const toggleDirection = extension.enabled ? 'toggleOff' : 'toggleOn';
|
||||||
const extensionConfig = extractExtensionConfig(extension);
|
const extensionConfig = extractExtensionConfig(extension);
|
||||||
|
|
||||||
await toggleExtension({
|
try {
|
||||||
toggle: toggleDirection,
|
await toggleExtension({
|
||||||
extensionConfig: extensionConfig,
|
toggle: toggleDirection,
|
||||||
addToConfig: addExtension,
|
extensionConfig: extensionConfig,
|
||||||
toastOptions: { silent: false },
|
addToConfig: addExtension,
|
||||||
});
|
toastOptions: { silent: false },
|
||||||
|
});
|
||||||
|
|
||||||
await fetchExtensions(); // Refresh the list after toggling
|
await fetchExtensions(); // Refresh the list after successful toggle
|
||||||
|
return true; // Indicate success
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Toggle extension failed:', error);
|
||||||
|
// Don't refresh the extension list on failure - this allows our visual state rollback to work
|
||||||
|
// The actual state in the config hasn't changed anyway
|
||||||
|
throw error; // Re-throw to let the ExtensionItem component know it failed
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleConfigureClick = (extension: FixedExtensionEntry) => {
|
const handleConfigureClick = (extension: FixedExtensionEntry) => {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// ExtensionItem.tsx
|
import React, { useState, useEffect } from 'react';
|
||||||
import React from 'react';
|
|
||||||
import { Switch } from '../../../ui/switch';
|
import { Switch } from '../../../ui/switch';
|
||||||
import { Gear } from '../../../icons/Gear';
|
import { Gear } from '../../../icons/Gear';
|
||||||
import { FixedExtensionEntry } from '../../../ConfigContext';
|
import { FixedExtensionEntry } from '../../../ConfigContext';
|
||||||
@@ -7,11 +6,46 @@ import { getSubtitle, getFriendlyTitle } from './ExtensionList';
|
|||||||
|
|
||||||
interface ExtensionItemProps {
|
interface ExtensionItemProps {
|
||||||
extension: FixedExtensionEntry;
|
extension: FixedExtensionEntry;
|
||||||
onToggle: (extension: FixedExtensionEntry) => void;
|
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void>;
|
||||||
onConfigure: (extension: FixedExtensionEntry) => void;
|
onConfigure: (extension: FixedExtensionEntry) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ExtensionItem({ extension, onToggle, onConfigure }: ExtensionItemProps) {
|
export default function ExtensionItem({ extension, onToggle, onConfigure }: ExtensionItemProps) {
|
||||||
|
// Add local state to track the visual toggle state
|
||||||
|
const [visuallyEnabled, setVisuallyEnabled] = useState(extension.enabled);
|
||||||
|
// Track if we're in the process of toggling
|
||||||
|
const [isToggling, setIsToggling] = useState(false);
|
||||||
|
|
||||||
|
const handleToggle = async (ext: FixedExtensionEntry) => {
|
||||||
|
// Prevent multiple toggles while one is in progress
|
||||||
|
if (isToggling) return;
|
||||||
|
|
||||||
|
setIsToggling(true);
|
||||||
|
|
||||||
|
// Immediately update visual state
|
||||||
|
const newState = !ext.enabled;
|
||||||
|
setVisuallyEnabled(newState);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Call the actual toggle function that performs the async operation
|
||||||
|
await onToggle(ext);
|
||||||
|
// Success case is handled by the useEffect below when extension.enabled changes
|
||||||
|
} catch (error) {
|
||||||
|
// If there was an error, revert the visual state
|
||||||
|
console.log('Toggle failed, reverting visual state');
|
||||||
|
setVisuallyEnabled(!newState);
|
||||||
|
} finally {
|
||||||
|
setIsToggling(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update visual state when the actual extension state changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isToggling) {
|
||||||
|
setVisuallyEnabled(extension.enabled);
|
||||||
|
}
|
||||||
|
}, [extension.enabled, isToggling]);
|
||||||
|
|
||||||
const renderFormattedSubtitle = () => {
|
const renderFormattedSubtitle = () => {
|
||||||
const subtitle = getSubtitle(extension);
|
const subtitle = getSubtitle(extension);
|
||||||
return subtitle.split('\n').map((part, index) => (
|
return subtitle.split('\n').map((part, index) => (
|
||||||
@@ -21,6 +55,7 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
|
|||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-borderSubtle p-4 mb-2">
|
<div className="rounded-lg border border-borderSubtle p-4 mb-2">
|
||||||
<div className="flex items-center justify-between mb-2">
|
<div className="flex items-center justify-between mb-2">
|
||||||
@@ -36,8 +71,8 @@ export default function ExtensionItem({ extension, onToggle, onConfigure }: Exte
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Switch
|
<Switch
|
||||||
checked={extension.enabled}
|
checked={(isToggling && visuallyEnabled) || extension.enabled}
|
||||||
onCheckedChange={() => onToggle(extension)}
|
onCheckedChange={() => handleToggle(extension)}
|
||||||
variant="mono"
|
variant="mono"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { combineCmdAndArgs } from '../utils';
|
|||||||
|
|
||||||
interface ExtensionListProps {
|
interface ExtensionListProps {
|
||||||
extensions: FixedExtensionEntry[];
|
extensions: FixedExtensionEntry[];
|
||||||
onToggle: (extension: FixedExtensionEntry) => void;
|
onToggle: (extension: FixedExtensionEntry) => Promise<boolean | void>;
|
||||||
onConfigure: (extension: FixedExtensionEntry) => void;
|
onConfigure: (extension: FixedExtensionEntry) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,7 +25,6 @@ export default function ExtensionList({ extensions, onToggle, onConfigure }: Ext
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
// Helper function to get a friendly title from extension name
|
// Helper function to get a friendly title from extension name
|
||||||
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
export function getFriendlyTitle(extension: FixedExtensionEntry): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user